diff options
Diffstat (limited to 'tools/linguist/lupdate')
-rw-r--r-- | tools/linguist/lupdate/cpp.cpp | 1919 | ||||
-rw-r--r-- | tools/linguist/lupdate/java.cpp | 643 | ||||
-rw-r--r-- | tools/linguist/lupdate/lupdate.1 | 14 | ||||
-rw-r--r-- | tools/linguist/lupdate/lupdate.h | 85 | ||||
-rw-r--r-- | tools/linguist/lupdate/lupdate.pro | 15 | ||||
-rw-r--r-- | tools/linguist/lupdate/main.cpp | 125 | ||||
-rw-r--r-- | tools/linguist/lupdate/merge.cpp | 505 | ||||
-rw-r--r-- | tools/linguist/lupdate/qscript.cpp | 2391 | ||||
-rw-r--r-- | tools/linguist/lupdate/qscript.g | 2026 | ||||
-rw-r--r-- | tools/linguist/lupdate/ui.cpp | 205 |
10 files changed, 7870 insertions, 58 deletions
diff --git a/tools/linguist/lupdate/cpp.cpp b/tools/linguist/lupdate/cpp.cpp new file mode 100644 index 0000000..58e094b --- /dev/null +++ b/tools/linguist/lupdate/cpp.cpp @@ -0,0 +1,1919 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Linguist of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lupdate.h" + +#include <translator.h> + +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> +#include <QtCore/QStack> +#include <QtCore/QString> +#include <QtCore/QTextCodec> +#include <QtCore/QTextStream> + +#include <ctype.h> // for isXXX() + +QT_BEGIN_NAMESPACE + +/* qmake ignore Q_OBJECT */ + +static const char MagicComment[] = "TRANSLATOR "; + +#define STRINGIFY_INTERNAL(x) #x +#define STRINGIFY(x) STRINGIFY_INTERNAL(x) +#define STRING(s) static QString str##s(QLatin1String(STRINGIFY(s))) + +//#define DIAGNOSE_RETRANSLATABILITY // FIXME: should make a runtime option of this + +uint qHash(const QStringList &qsl) +{ + uint hash = 0; + foreach (const QString &qs, qsl) { + hash ^= qHash(qs) ^ 0xa09df22f; + hash = (hash << 13) | (hash >> 19); + } + return hash; +} + +struct Namespace { + + Namespace() : + isClass(false), + hasTrFunctions(false), needsTrFunctions(false), complained(false) + {} + + QString name; + QMap<QString, Namespace *> children; + QMap<QString, QStringList> aliases; + QSet<QStringList> usings; + + int fileId; + + bool isClass; + + bool hasTrFunctions; + bool needsTrFunctions; + bool complained; // ... that tr functions are missing. +}; + +typedef QList<Namespace *> NamespaceList; + +struct ParseResults { + + ParseResults() + { + static int nextFileId; + rootNamespace.fileId = nextFileId++; + tor = 0; + } + bool detachNamespace(Namespace **that); + Namespace *include(Namespace *that, const Namespace *other); + void unite(const ParseResults *other); + + Namespace rootNamespace; + Translator *tor; + QSet<QString> allIncludes; +}; + +typedef QHash<QString, const ParseResults *> ParseResultHash; + +class CppFiles { + +public: + static const ParseResults *getResults(const QString &cleanFile); + static void setResults(const QString &cleanFile, const ParseResults *results); + static bool isBlacklisted(const QString &cleanFile); + static void setBlacklisted(const QString &cleanFile); + +private: + static ParseResultHash &parsedFiles(); + static QSet<QString> &blacklistedFiles(); +}; + +class CppParser { + +public: + CppParser(ParseResults *results = 0); + void setInput(const QString &in); + void setInput(QTextStream &ts, const QString &fileName); + void setTranslator(Translator *tor) { results->tor = tor; } + void parse(const QString &initialContext, ConversionData &cd, QSet<QString> &inclusions); + void parseInternal(ConversionData &cd, QSet<QString> &inclusions); + const ParseResults *getResults() const { return results; } + void deleteResults() { delete results; } + + struct SavedState { + QStringList namespaces; + QStack<int> namespaceDepths; + QStringList functionContext; + QString functionContextUnresolved; + QString pendingContext; + }; + +private: + struct IfdefState { + IfdefState() {} + IfdefState(int _braceDepth, int _parenDepth) : + braceDepth(_braceDepth), + parenDepth(_parenDepth), + elseLine(-1) + {} + + SavedState state; + int braceDepth, braceDepth1st; + int parenDepth, parenDepth1st; + int elseLine; + }; + + uint getChar(); + uint getToken(); + bool match(uint t); + bool matchString(QString *s); + bool matchEncoding(bool *utf8); + bool matchInteger(qlonglong *number); + bool matchStringOrNull(QString *s); + bool matchExpression(); + + QString transcode(const QString &str, bool utf8); + void recordMessage( + int line, const QString &context, const QString &text, const QString &comment, + const QString &extracomment, const QString &msgid, const TranslatorMessage::ExtraData &extra, + bool utf8, bool plural); + + void processInclude(const QString &file, ConversionData &cd, + QSet<QString> &inclusions); + + void saveState(SavedState *state); + void loadState(const SavedState *state); + + static QString stringifyNamespace(const NamespaceList &namespaces); + static QStringList stringListifyNamespace(const NamespaceList &namespaces); + void modifyNamespace(NamespaceList *namespaces); + NamespaceList resolveNamespaces(const QStringList &segments); + bool qualifyOne(const NamespaceList &namespaces, int nsIdx, const QString &segment, + NamespaceList *resolved); + bool fullyQualify(const NamespaceList &namespaces, const QStringList &segments, + bool isDeclaration, + NamespaceList *resolved, QStringList *unresolved); + void enterNamespace(NamespaceList *namespaces, const QString &name); + void truncateNamespaces(NamespaceList *namespaces, int lenght); + + enum { + Tok_Eof, Tok_class, Tok_friend, Tok_namespace, Tok_using, Tok_return, + Tok_tr = 10, Tok_trUtf8, Tok_translate, Tok_translateUtf8, Tok_trid, + Tok_Q_OBJECT = 20, Tok_Q_DECLARE_TR_FUNCTIONS, + Tok_Ident, Tok_Comment, Tok_String, Tok_Arrow, Tok_Colon, Tok_ColonColon, + Tok_Equals, + Tok_LeftBrace = 30, Tok_RightBrace, Tok_LeftParen, Tok_RightParen, Tok_Comma, Tok_Semicolon, + Tok_Integer = 40, + Tok_QuotedInclude = 50, Tok_AngledInclude, + Tok_Other = 99 + }; + + // Tokenizer state + QString yyFileName; + int yyCh; + bool yyAtNewline; + bool yyCodecIsUtf8; + bool yyForceUtf8; + QString yyIdent; + QString yyComment; + QString yyString; + qlonglong yyInteger; + QStack<IfdefState> yyIfdefStack; + int yyBraceDepth; + int yyParenDepth; + int yyLineNo; + int yyCurLineNo; + int yyBraceLineNo; + int yyParenLineNo; + + // the string to read from and current position in the string + QTextCodec *yySourceCodec; + bool yySourceIsUnicode; + QString yyInStr; + int yyInPos; + + // Parser state + uint yyTok; + + NamespaceList namespaces; + QStack<int> namespaceDepths; + NamespaceList functionContext; + QString functionContextUnresolved; + QString prospectiveContext; + QString pendingContext; + ParseResults *results; + bool directInclude; + + SavedState savedState; + int yyMinBraceDepth; + bool inDefine; +}; + +CppParser::CppParser(ParseResults *_results) +{ + if (_results) { + results = _results; + directInclude = true; + } else { + results = new ParseResults; + directInclude = false; + } + yyInPos = 0; + yyBraceDepth = 0; + yyParenDepth = 0; + yyCurLineNo = 1; + yyBraceLineNo = 1; + yyParenLineNo = 1; + yyAtNewline = true; + yyMinBraceDepth = 0; + inDefine = false; +} + +void CppParser::setInput(const QString &in) +{ + yyInStr = in; + yyFileName = QString(); + yySourceCodec = 0; + yySourceIsUnicode = true; + yyForceUtf8 = true; +} + +void CppParser::setInput(QTextStream &ts, const QString &fileName) +{ + yyInStr = ts.readAll(); + yyFileName = fileName; + yySourceCodec = ts.codec(); + yySourceIsUnicode = yySourceCodec->name().startsWith("UTF-"); + yyForceUtf8 = false; +} + +/* + The first part of this source file is the C++ tokenizer. We skip + most of C++; the only tokens that interest us are defined here. + Thus, the code fragment + + int main() + { + printf("Hello, world!\n"); + return 0; + } + + is broken down into the following tokens (Tok_ omitted): + + Ident Ident LeftParen RightParen + LeftBrace + Ident LeftParen String RightParen Semicolon + return Semicolon + RightBrace. + + The 0 doesn't produce any token. +*/ + +uint CppParser::getChar() +{ + forever { + if (yyInPos >= yyInStr.size()) + return EOF; + uint c = yyInStr[yyInPos++].unicode(); + if (c == '\\' && yyInPos < yyInStr.size()) { + if (yyInStr[yyInPos].unicode() == '\n') { + ++yyCurLineNo; + ++yyInPos; + continue; + } + if (yyInStr[yyInPos].unicode() == '\r') { + ++yyCurLineNo; + ++yyInPos; + if (yyInPos < yyInStr.size() && yyInStr[yyInPos].unicode() == '\n') + ++yyInPos; + continue; + } + } + if (c == '\r') { + if (yyInPos < yyInStr.size() && yyInStr[yyInPos].unicode() == '\n') + ++yyInPos; + c = '\n'; + ++yyCurLineNo; + yyAtNewline = true; + } else if (c == '\n') { + ++yyCurLineNo; + yyAtNewline = true; + } else if (c != ' ' && c != '\t' && c != '#') { + yyAtNewline = false; + } + return c; + } +} + +uint CppParser::getToken() +{ + restart: + yyIdent.clear(); + yyComment.clear(); + yyString.clear(); + + while (yyCh != EOF) { + yyLineNo = yyCurLineNo; + + if (yyCh == '#' && yyAtNewline) { + /* + Early versions of lupdate complained about + unbalanced braces in the following code: + + #ifdef ALPHA + while (beta) { + #else + while (gamma) { + #endif + delta; + } + + The code contains, indeed, two opening braces for + one closing brace; yet there's no reason to panic. + + The solution is to remember yyBraceDepth as it was + when #if, #ifdef or #ifndef was met, and to set + yyBraceDepth to that value when meeting #elif or + #else. + */ + do { + yyCh = getChar(); + } while (isspace(yyCh) && yyCh != '\n'); + + switch (yyCh) { + case 'd': // define + // Skip over the name of the define to avoid it being interpreted as c++ code + do { // Rest of "define" + yyCh = getChar(); + if (yyCh == EOF) + return Tok_Eof; + if (yyCh == '\n') + goto restart; + } while (!isspace(yyCh)); + do { // Space beween "define" and macro name + yyCh = getChar(); + if (yyCh == EOF) + return Tok_Eof; + if (yyCh == '\n') + goto restart; + } while (isspace(yyCh)); + do { // Macro name + if (yyCh == '(') { + // Argument list. Follows the name without a space, and no + // paren nesting is possible. + do { + yyCh = getChar(); + if (yyCh == EOF) + return Tok_Eof; + if (yyCh == '\n') + goto restart; + } while (yyCh != ')'); + break; + } + yyCh = getChar(); + if (yyCh == EOF) + return Tok_Eof; + if (yyCh == '\n') + goto restart; + } while (!isspace(yyCh)); + do { // Shortcut the immediate newline case if no comments follow. + yyCh = getChar(); + if (yyCh == EOF) + return Tok_Eof; + if (yyCh == '\n') + goto restart; + } while (isspace(yyCh)); + + saveState(&savedState); + yyMinBraceDepth = yyBraceDepth; + inDefine = true; + goto restart; + case 'i': + yyCh = getChar(); + if (yyCh == 'f') { + // if, ifdef, ifndef + yyIfdefStack.push(IfdefState(yyBraceDepth, yyParenDepth)); + yyCh = getChar(); + } else if (yyCh == 'n') { + // include + do { + yyCh = getChar(); + } while (yyCh != EOF && !isspace(yyCh)); + do { + yyCh = getChar(); + } while (isspace(yyCh)); + int tChar; + if (yyCh == '"') + tChar = '"'; + else if (yyCh == '<') + tChar = '>'; + else + break; + forever { + yyCh = getChar(); + if (yyCh == EOF || yyCh == '\n') + break; + if (yyCh == tChar) { + yyCh = getChar(); + break; + } + yyString += yyCh; + } + return (tChar == '"') ? Tok_QuotedInclude : Tok_AngledInclude; + } + break; + case 'e': + yyCh = getChar(); + if (yyCh == 'l') { + // elif, else + if (!yyIfdefStack.isEmpty()) { + IfdefState &is = yyIfdefStack.top(); + if (is.elseLine != -1) { + if (yyBraceDepth != is.braceDepth1st || yyParenDepth != is.parenDepth1st) + qWarning("%s:%d: Parenthesis/brace mismatch between " + "#if and #else branches; using #if branch\n", + qPrintable(yyFileName), is.elseLine); + } else { + is.braceDepth1st = yyBraceDepth; + is.parenDepth1st = yyParenDepth; + saveState(&is.state); + } + is.elseLine = yyLineNo; + yyBraceDepth = is.braceDepth; + yyParenDepth = is.parenDepth; + } + yyCh = getChar(); + } else if (yyCh == 'n') { + // endif + if (!yyIfdefStack.isEmpty()) { + IfdefState is = yyIfdefStack.pop(); + if (is.elseLine != -1) { + if (yyBraceDepth != is.braceDepth1st || yyParenDepth != is.parenDepth1st) + qWarning("%s:%d: Parenthesis/brace mismatch between " + "#if and #else branches; using #if branch\n", + qPrintable(yyFileName), is.elseLine); + yyBraceDepth = is.braceDepth1st; + yyParenDepth = is.parenDepth1st; + loadState(&is.state); + } + } + yyCh = getChar(); + } + break; + } + // Optimization: skip over rest of preprocessor directive + do { + if (yyCh == '/') { + yyCh = getChar(); + if (yyCh == '/') { + do { + yyCh = getChar(); + } while (yyCh != EOF && yyCh != '\n'); + break; + } else if (yyCh == '*') { + bool metAster = false; + + forever { + yyCh = getChar(); + if (yyCh == EOF) { + qWarning("%s:%d: Unterminated C++ comment\n", + qPrintable(yyFileName), yyLineNo); + break; + } + + if (yyCh == '*') { + metAster = true; + } else if (metAster && yyCh == '/') { + yyCh = getChar(); + break; + } else { + metAster = false; + } + } + } + } else { + yyCh = getChar(); + } + } while (yyCh != '\n' && yyCh != EOF); + yyCh = getChar(); + } else if (isalpha(yyCh) || yyCh == '_') { + do { + yyIdent += yyCh; + yyCh = getChar(); + } while (isalnum(yyCh) || yyCh == '_'); + + //qDebug() << "IDENT: " << yyIdent; + + switch (yyIdent.at(0).unicode()) { + case 'Q': + if (yyIdent == QLatin1String("Q_OBJECT")) + return Tok_Q_OBJECT; + if (yyIdent == QLatin1String("Q_DECLARE_TR_FUNCTIONS")) + return Tok_Q_DECLARE_TR_FUNCTIONS; + if (yyIdent == QLatin1String("QT_TR_NOOP")) + return Tok_tr; + if (yyIdent == QLatin1String("QT_TRID_NOOP")) + return Tok_trid; + if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP")) + return Tok_translate; + if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3")) + return Tok_translate; + if (yyIdent == QLatin1String("QT_TR_NOOP_UTF8")) + return Tok_trUtf8; + if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP_UTF8")) + return Tok_translateUtf8; + if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3_UTF8")) + return Tok_translateUtf8; + break; + case 'T': + // TR() for when all else fails + if (yyIdent.compare(QLatin1String("TR"), Qt::CaseInsensitive) == 0) { + return Tok_tr; + } + break; + case 'c': + if (yyIdent == QLatin1String("class")) + return Tok_class; + break; + case 'f': + /* + QTranslator::findMessage() has the same parameters as + QApplication::translate(). + */ + if (yyIdent == QLatin1String("findMessage")) + return Tok_translate; + if (yyIdent == QLatin1String("friend")) + return Tok_friend; + break; + case 'n': + if (yyIdent == QLatin1String("namespace")) + return Tok_namespace; + break; + case 'q': + if (yyIdent == QLatin1String("qtTrId")) + return Tok_trid; + break; + case 'r': + if (yyIdent == QLatin1String("return")) + return Tok_return; + break; + case 's': + if (yyIdent == QLatin1String("struct")) + return Tok_class; + break; + case 't': + if (yyIdent == QLatin1String("tr")) { + return Tok_tr; + } + if (yyIdent == QLatin1String("trUtf8")) { + return Tok_trUtf8; + } + if (yyIdent == QLatin1String("translate")) { + return Tok_translate; + } + break; + case 'u': + if (yyIdent == QLatin1String("using")) + return Tok_using; + break; + } + return Tok_Ident; + } else { + switch (yyCh) { + case '\n': + if (inDefine) { + loadState(&savedState); + prospectiveContext.clear(); + yyBraceDepth = yyMinBraceDepth; + yyMinBraceDepth = 0; + inDefine = false; + } + yyCh = getChar(); + break; + case '/': + yyCh = getChar(); + if (yyCh == '/') { + do { + yyCh = getChar(); + if (yyCh == EOF) + break; + yyComment.append(yyCh); + } while (yyCh != '\n'); + } else if (yyCh == '*') { + bool metAster = false; + + forever { + yyCh = getChar(); + if (yyCh == EOF) { + qWarning("%s:%d: Unterminated C++ comment\n", + qPrintable(yyFileName), yyLineNo); + return Tok_Comment; + } + yyComment.append(yyCh); + + if (yyCh == '*') + metAster = true; + else if (metAster && yyCh == '/') + break; + else + metAster = false; + } + yyCh = getChar(); + yyComment.chop(2); + } + return Tok_Comment; + case '"': + yyCh = getChar(); + while (yyCh != EOF && yyCh != '\n' && yyCh != '"') { + if (yyCh == '\\') { + yyCh = getChar(); + if (yyCh == EOF || yyCh == '\n') + break; + yyString.append(QLatin1Char('\\')); + } + yyString.append(yyCh); + yyCh = getChar(); + } + + if (yyCh != '"') + qWarning("%s:%d: Unterminated C++ string\n", + qPrintable(yyFileName), yyLineNo); + else + yyCh = getChar(); + return Tok_String; + case '-': + yyCh = getChar(); + if (yyCh == '>') { + yyCh = getChar(); + return Tok_Arrow; + } + break; + case ':': + yyCh = getChar(); + if (yyCh == ':') { + yyCh = getChar(); + return Tok_ColonColon; + } + return Tok_Colon; + // Incomplete: '<' might be part of '<=' or of template syntax. + // The main intent of not completely ignoring it is to break + // parsing of things like std::cout << QObject::tr() as + // context std::cout::QObject (see Task 161106) + case '=': + yyCh = getChar(); + return Tok_Equals; + case '>': + case '<': + yyCh = getChar(); + return Tok_Other; + case '\'': + yyCh = getChar(); + if (yyCh == '\\') + yyCh = getChar(); + + forever { + if (yyCh == EOF || yyCh == '\n') { + qWarning("%s:%d: Unterminated C++ character\n", + qPrintable(yyFileName), yyLineNo); + break; + } + yyCh = getChar(); + if (yyCh == '\'') { + yyCh = getChar(); + break; + } + } + break; + case '{': + if (yyBraceDepth == 0) + yyBraceLineNo = yyCurLineNo; + yyBraceDepth++; + yyCh = getChar(); + return Tok_LeftBrace; + case '}': + if (yyBraceDepth == yyMinBraceDepth) { + if (!inDefine) + qWarning("%s:%d: Excess closing brace in C++ code" + " (or abuse of the C++ preprocessor)\n", + qPrintable(yyFileName), yyCurLineNo); + // Avoid things getting messed up even more + yyCh = getChar(); + return Tok_Semicolon; + } + yyBraceDepth--; + yyCh = getChar(); + return Tok_RightBrace; + case '(': + if (yyParenDepth == 0) + yyParenLineNo = yyCurLineNo; + yyParenDepth++; + yyCh = getChar(); + return Tok_LeftParen; + case ')': + if (yyParenDepth == 0) + qWarning("%s:%d: Excess closing parenthesis in C++ code" + " (or abuse of the C++ preprocessor)\n", + qPrintable(yyFileName), yyCurLineNo); + else + yyParenDepth--; + yyCh = getChar(); + return Tok_RightParen; + case ',': + yyCh = getChar(); + return Tok_Comma; + case ';': + yyCh = getChar(); + return Tok_Semicolon; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + QByteArray ba; + ba += yyCh; + yyCh = getChar(); + bool hex = yyCh == 'x'; + if (hex) { + ba += yyCh; + yyCh = getChar(); + } + while (hex ? isxdigit(yyCh) : isdigit(yyCh)) { + ba += yyCh; + yyCh = getChar(); + } + bool ok; + yyInteger = ba.toLongLong(&ok); + if (ok) + return Tok_Integer; + break; + } + default: + yyCh = getChar(); + break; + } + } + } + return Tok_Eof; +} + +/* + The second part of this source file are namespace/class related + utilities for the third part. +*/ + +void CppParser::saveState(SavedState *state) +{ + state->namespaces = stringListifyNamespace(namespaces); + state->namespaceDepths = namespaceDepths; + state->functionContext = stringListifyNamespace(functionContext); + state->functionContextUnresolved = functionContextUnresolved; + state->pendingContext = pendingContext; +} + +void CppParser::loadState(const SavedState *state) +{ + namespaces = resolveNamespaces(state->namespaces); + namespaceDepths = state->namespaceDepths; + functionContext = resolveNamespaces(state->functionContext); + functionContextUnresolved = state->functionContextUnresolved; + pendingContext = state->pendingContext; +} + +bool ParseResults::detachNamespace(Namespace **that) +{ + if ((*that)->fileId != rootNamespace.fileId) { + Namespace *newThat = new Namespace; + *newThat = **that; + newThat->fileId = rootNamespace.fileId; + *that = newThat; + return true; + } + return false; +} + +Namespace *ParseResults::include(Namespace *that, const Namespace *other) +{ + Namespace *origThat = that; + foreach (Namespace *otherSub, other->children) { + if (Namespace *thisSub = that->children.value(otherSub->name)) { + // Don't make these cause a detach - it's best + // (though not necessary) if they are shared + thisSub->isClass |= otherSub->isClass; + thisSub->hasTrFunctions |= otherSub->hasTrFunctions; + thisSub->needsTrFunctions |= otherSub->needsTrFunctions; + thisSub->complained |= otherSub->complained; + + if (Namespace *newSub = include(thisSub, otherSub)) { + thisSub = newSub; + detachNamespace(&that); + that->children[thisSub->name] = thisSub; + } + } else { + detachNamespace(&that); + that->children[otherSub->name] = otherSub; + } + } + if ((that->aliases != other->aliases && !other->aliases.isEmpty()) + || (that->usings != other->usings && !other->usings.isEmpty())) { + detachNamespace(&that); + that->aliases.unite(other->aliases); + that->usings.unite(other->usings); + } + return (that != origThat) ? that : 0; +} + +void ParseResults::unite(const ParseResults *other) +{ + allIncludes.unite(other->allIncludes); + include(&rootNamespace, &other->rootNamespace); +} + +void CppParser::modifyNamespace(NamespaceList *namespaces) +{ + Namespace *pns = 0; + int i = namespaces->count(); + forever { + --i; + Namespace *ns = namespaces->at(i); + bool detached = results->detachNamespace(&ns); + if (pns) + ns->children[pns->name] = pns; + if (!detached) // Known to be true for root namespace + return; + pns = ns; + namespaces->replace(i, ns); + } +} + +QString CppParser::stringifyNamespace(const NamespaceList &namespaces) +{ + QString ret; + for (int i = 1; i < namespaces.count(); ++i) { + if (i > 1) + ret += QLatin1String("::"); + ret += namespaces.at(i)->name; + } + return ret; +} + +QStringList CppParser::stringListifyNamespace(const NamespaceList &namespaces) +{ + QStringList ret; + for (int i = 1; i < namespaces.count(); ++i) + ret << namespaces.at(i)->name; + return ret; +} + +// This function is called only with known-existing namespaces +NamespaceList CppParser::resolveNamespaces(const QStringList &segments) +{ + NamespaceList ret; + Namespace *ns = &results->rootNamespace; + ret << ns; + foreach (const QString &seg, segments) { + ns = ns->children.value(seg); + ret << ns; + } + return ret; +} + +bool CppParser::qualifyOne(const NamespaceList &namespaces, int nsIdx, const QString &segment, + NamespaceList *resolved) +{ + const Namespace *ns = namespaces.at(nsIdx); + QMap<QString, Namespace *>::ConstIterator cnsi = ns->children.constFind(segment); + if (cnsi != ns->children.constEnd()) { + *resolved = namespaces.mid(0, nsIdx + 1); + *resolved << *cnsi; + return true; + } + QMap<QString, QStringList>::ConstIterator nsai = ns->aliases.constFind(segment); + if (nsai != ns->aliases.constEnd()) { + *resolved = resolveNamespaces(*nsai); + return true; + } + foreach (const QStringList &use, ns->usings) { + NamespaceList usedNs = resolveNamespaces(use); + if (qualifyOne(usedNs, usedNs.count() - 1, segment, resolved)) + return true; + } + return false; +} + +bool CppParser::fullyQualify(const NamespaceList &namespaces, const QStringList &segments, + bool isDeclaration, + NamespaceList *resolved, QStringList *unresolved) +{ + int nsIdx; + int initSegIdx; + + if (segments.first().isEmpty()) { + // fully qualified + if (segments.count() == 1) { + resolved->clear(); + *resolved << &results->rootNamespace; + return true; + } + initSegIdx = 1; + nsIdx = 0; + } else { + initSegIdx = 0; + nsIdx = namespaces.count() - 1; + } + + do { + if (qualifyOne(namespaces, nsIdx, segments[initSegIdx], resolved)) { + int segIdx = initSegIdx; + while (++segIdx < segments.count()) { + if (!qualifyOne(*resolved, resolved->count() - 1, segments[segIdx], resolved)) { + if (unresolved) + *unresolved = segments.mid(segIdx); + return false; + } + } + return true; + } + } while (!isDeclaration && --nsIdx >= 0); + resolved->clear(); + *resolved << &results->rootNamespace; + if (unresolved) + *unresolved = segments.mid(initSegIdx); + return false; +} + +void CppParser::enterNamespace(NamespaceList *namespaces, const QString &name) +{ + Namespace *ns = namespaces->last()->children.value(name); + if (!ns) { + ns = new Namespace; + ns->fileId = results->rootNamespace.fileId; + ns->name = name; + modifyNamespace(namespaces); + namespaces->last()->children[name] = ns; + } + *namespaces << ns; +} + +void CppParser::truncateNamespaces(NamespaceList *namespaces, int length) +{ + if (namespaces->count() > length) + namespaces->erase(namespaces->begin() + length, namespaces->end()); +} + +/* + Functions for processing include files. +*/ + +ParseResultHash &CppFiles::parsedFiles() +{ + static ParseResultHash parsed; + + return parsed; +} + +QSet<QString> &CppFiles::blacklistedFiles() +{ + static QSet<QString> blacklisted; + + return blacklisted; +} + +const ParseResults *CppFiles::getResults(const QString &cleanFile) +{ + ParseResultHash::ConstIterator it = parsedFiles().find(cleanFile); + if (it == parsedFiles().constEnd()) + return 0; + return *it; +} + +void CppFiles::setResults(const QString &cleanFile, const ParseResults *results) +{ + parsedFiles().insert(cleanFile, results); +} + +bool CppFiles::isBlacklisted(const QString &cleanFile) +{ + return blacklistedFiles().contains(cleanFile); +} + +void CppFiles::setBlacklisted(const QString &cleanFile) +{ + blacklistedFiles().insert(cleanFile); +} + +void CppParser::processInclude(const QString &file, ConversionData &cd, + QSet<QString> &inclusions) +{ + QString cleanFile = QDir::cleanPath(file); + + if (inclusions.contains(cleanFile)) { + qWarning("%s:%d: circular inclusion of %s\n", + qPrintable(yyFileName), yyLineNo, qPrintable(cleanFile)); + return; + } + + // If the #include is in any kind of namespace, has been blacklisted previously, + // or is not a header file (stdc++ extensionless or *.h*), then really include + // it. Otherwise it is safe to process it stand-alone and re-use the parsed + // namespace data for inclusion into other files. + bool isIndirect = false; + if (namespaces.count() == 1 && functionContext.count() == 1 + && functionContextUnresolved.isEmpty() && pendingContext.isEmpty() + && !CppFiles::isBlacklisted(cleanFile)) { + QString fileExt = QFileInfo(cleanFile).suffix(); + if (fileExt.isEmpty() || fileExt.startsWith(QLatin1Char('h'), Qt::CaseInsensitive)) { + + if (results->allIncludes.contains(cleanFile)) + return; + results->allIncludes.insert(cleanFile); + + if (const ParseResults *res = CppFiles::getResults(cleanFile)) { + results->unite(res); + return; + } + + isIndirect = true; + } + } + + QFile f(cleanFile); + if (!f.open(QIODevice::ReadOnly)) { + qWarning("%s:%d: Cannot open %s: %s\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(cleanFile), qPrintable(f.errorString())); + return; + } + + QTextStream ts(&f); + ts.setCodec(yySourceCodec); + ts.setAutoDetectUnicode(true); + + inclusions.insert(cleanFile); + if (isIndirect) { + CppParser parser; + foreach (const QString &projectRoot, cd.m_projectRoots) + if (cleanFile.startsWith(projectRoot)) { + parser.setTranslator(new Translator); + break; + } + parser.setInput(ts, cleanFile); + parser.parse(cd.m_defaultContext, cd, inclusions); + CppFiles::setResults(cleanFile, parser.getResults()); + results->unite(parser.results); + } else { + CppParser parser(results); + parser.namespaces = namespaces; + parser.functionContext = functionContext; + parser.functionContextUnresolved = functionContextUnresolved; + parser.pendingContext = pendingContext; + parser.setInput(ts, cleanFile); + parser.parseInternal(cd, inclusions); + // Don't wreak havoc if not enough braces were found. + truncateNamespaces(&parser.namespaces, namespaces.count()); + truncateNamespaces(&parser.functionContext, functionContext.count()); + // Copy them back - the pointers might have changed. + namespaces = parser.namespaces; + functionContext = parser.functionContext; + // Avoid that messages obtained by direct scanning are used + CppFiles::setBlacklisted(cleanFile); + } + inclusions.remove(cleanFile); +} + +/* + The third part of this source file is the parser. It accomplishes + a very easy task: It finds all strings inside a tr() or translate() + call, and possibly finds out the context of the call. It supports + three cases: (1) the context is specified, as in + FunnyDialog::tr("Hello") or translate("FunnyDialog", "Hello"); + (2) the call appears within an inlined function; (3) the call + appears within a function defined outside the class definition. +*/ + +bool CppParser::match(uint t) +{ + bool matches = (yyTok == t); + if (matches) + yyTok = getToken(); + return matches; +} + +bool CppParser::matchString(QString *s) +{ + bool matches = (yyTok == Tok_String); + s->clear(); + while (yyTok == Tok_String) { + *s += yyString; + do { + yyTok = getToken(); + } while (yyTok == Tok_Comment); + } + return matches; +} + +bool CppParser::matchEncoding(bool *utf8) +{ + STRING(QApplication); + STRING(QCoreApplication); + STRING(UnicodeUTF8); + STRING(DefaultCodec); + STRING(CodecForTr); + + if (yyTok != Tok_Ident) + return false; + if (yyIdent == strQApplication || yyIdent == strQCoreApplication) { + yyTok = getToken(); + if (yyTok == Tok_ColonColon) + yyTok = getToken(); + } + if (yyIdent == strUnicodeUTF8) { + *utf8 = true; + yyTok = getToken(); + return true; + } + if (yyIdent == strDefaultCodec || yyIdent == strCodecForTr) { + *utf8 = false; + yyTok = getToken(); + return true; + } + return false; +} + +bool CppParser::matchInteger(qlonglong *number) +{ + bool matches = (yyTok == Tok_Integer); + if (matches) { + yyTok = getToken(); + *number = yyInteger; + } + return matches; +} + +bool CppParser::matchStringOrNull(QString *s) +{ + bool matches = matchString(s); + qlonglong num = 0; + if (!matches) + matches = matchInteger(&num); + return matches && num == 0; +} + +/* + * match any expression that can return a number, which can be + * 1. Literal number (e.g. '11') + * 2. simple identifier (e.g. 'm_count') + * 3. simple function call (e.g. 'size()' ) + * 4. function call on an object (e.g. 'list.size()') + * 5. function call on an object (e.g. 'list->size()') + * + * Other cases: + * size(2,4) + * list().size() + * list(a,b).size(2,4) + * etc... + */ +bool CppParser::matchExpression() +{ + if (match(Tok_Integer)) + return true; + + int parenlevel = 0; + while (match(Tok_Ident) || parenlevel > 0) { + if (yyTok == Tok_RightParen) { + if (parenlevel == 0) break; + --parenlevel; + yyTok = getToken(); + } else if (yyTok == Tok_LeftParen) { + yyTok = getToken(); + if (yyTok == Tok_RightParen) { + yyTok = getToken(); + } else { + ++parenlevel; + } + } else if (yyTok == Tok_Ident) { + continue; + } else if (yyTok == Tok_Arrow) { + yyTok = getToken(); + } else if (parenlevel == 0) { + return false; + } + } + return true; +} + +QString CppParser::transcode(const QString &str, bool utf8) +{ + static const char tab[] = "abfnrtv"; + static const char backTab[] = "\a\b\f\n\r\t\v"; + const QString in = (!utf8 || yySourceIsUnicode) + ? str : QString::fromUtf8(yySourceCodec->fromUnicode(str).data()); + QString out; + + out.reserve(in.length()); + for (int i = 0; i < in.length();) { + ushort c = in[i++].unicode(); + if (c == '\\') { + if (i >= in.length()) + break; + c = in[i++].unicode(); + + if (c == '\n') + continue; + + if (c == 'x') { + QByteArray hex; + while (i < in.length() && isxdigit((c = in[i].unicode()))) { + hex += c; + i++; + } + out += hex.toUInt(0, 16); + } else if (c >= '0' && c < '8') { + QByteArray oct; + int n = 0; + oct += c; + while (n < 2 && i < in.length() && (c = in[i].unicode()) >= '0' && c < '8') { + i++; + n++; + oct += c; + } + out += oct.toUInt(0, 8); + } else { + const char *p = strchr(tab, c); + out += QChar(QLatin1Char(!p ? c : backTab[p - tab])); + } + } else { + out += c; + } + } + return out; +} + +void CppParser::recordMessage( + int line, const QString &context, const QString &text, const QString &comment, + const QString &extracomment, const QString &msgid, const TranslatorMessage::ExtraData &extra, + bool utf8, bool plural) +{ + TranslatorMessage msg( + transcode(context, utf8), transcode(text, utf8), transcode(comment, utf8), QString(), + yyFileName, line, QStringList(), + TranslatorMessage::Unfinished, plural); + msg.setExtraComment(transcode(extracomment.simplified(), utf8)); + msg.setId(msgid); + msg.setExtras(extra); + if ((utf8 || yyForceUtf8) && !yyCodecIsUtf8 && msg.needs8Bit()) + msg.setUtf8(true); + results->tor->append(msg); +} + +void CppParser::parse(const QString &initialContext, ConversionData &cd, + QSet<QString> &inclusions) +{ + if (results->tor) + yyCodecIsUtf8 = (results->tor->codecName() == "UTF-8"); + + namespaces << &results->rootNamespace; + functionContext = namespaces; + functionContextUnresolved = initialContext; + + parseInternal(cd, inclusions); +} + +void CppParser::parseInternal(ConversionData &cd, QSet<QString> &inclusions) +{ + static QString strColons(QLatin1String("::")); + + QString context; + QString text; + QString comment; + QString extracomment; + QString msgid; + QString sourcetext; + TranslatorMessage::ExtraData extra; + QString prefix; +#ifdef DIAGNOSE_RETRANSLATABILITY + QString functionName; +#endif + int line; + bool utf8; + bool yyTokColonSeen = false; // Start of c'tor's initializer list + + yyCh = getChar(); + yyTok = getToken(); + while (yyTok != Tok_Eof) { + //qDebug() << "TOKEN: " << yyTok; + switch (yyTok) { + case Tok_QuotedInclude: { + text = QDir(QFileInfo(yyFileName).absolutePath()).absoluteFilePath(yyString); + if (QFileInfo(text).isFile()) { + processInclude(text, cd, inclusions); + yyTok = getToken(); + break; + } + } + /* fall through */ + case Tok_AngledInclude: { + QStringList cSources = cd.m_allCSources.values(yyString); + if (!cSources.isEmpty()) { + foreach (const QString &cSource, cSources) + processInclude(cSource, cd, inclusions); + goto incOk; + } + foreach (const QString &incPath, cd.m_includePath) { + text = QDir(incPath).absoluteFilePath(yyString); + if (QFileInfo(text).isFile()) { + processInclude(text, cd, inclusions); + goto incOk; + } + } + incOk: + yyTok = getToken(); + break; + } + case Tok_friend: + yyTok = getToken(); + // Ensure that these don't end up being interpreted as forward declarations + // (they are forwards, but with different namespacing). + if (yyTok == Tok_class) + yyTok = getToken(); + break; + case Tok_class: + yyTokColonSeen = false; + /* + Partial support for inlined functions. + */ + yyTok = getToken(); + if (yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) { + QStringList fct; + do { + /* + This code should execute only once, but we play + safe with impure definitions such as + 'class Q_EXPORT QMessageBox', in which case + 'QMessageBox' is the class name, not 'Q_EXPORT'. + */ + fct = QStringList(yyIdent); + yyTok = getToken(); + } while (yyTok == Tok_Ident); + while (yyTok == Tok_ColonColon) { + yyTok = getToken(); + if (yyTok != Tok_Ident) + break; // Oops ... + fct += yyIdent; + yyTok = getToken(); + } + if (fct.count() > 1) { + // Forward-declared class definitions can be namespaced + NamespaceList nsl; + if (!fullyQualify(namespaces, fct, true, &nsl, 0)) { + qWarning("%s:%d: Ignoring definition of undeclared qualified class\n", + qPrintable(yyFileName), yyLineNo); + break; + } + namespaceDepths.push(namespaces.count()); + namespaces = nsl; + } else { + namespaceDepths.push(namespaces.count()); + enterNamespace(&namespaces, fct.first()); + } + namespaces.last()->isClass = true; + + while (yyTok == Tok_Comment) + yyTok = getToken(); + if (yyTok == Tok_Colon) { + // Skip any token until '{' since lupdate might do things wrong if it finds + // a '::' token here. + do { + yyTok = getToken(); + } while (yyTok != Tok_LeftBrace && yyTok != Tok_Eof); + } else { + if (yyTok != Tok_LeftBrace) { + // Obviously a forward decl + truncateNamespaces(&namespaces, namespaceDepths.pop()); + break; + } + } + + functionContext = namespaces; + functionContextUnresolved.clear(); // Pointless + prospectiveContext.clear(); + pendingContext.clear(); + } + break; + case Tok_namespace: + yyTokColonSeen = false; + yyTok = getToken(); + if (yyTok == Tok_Ident) { + QString ns = yyIdent; + yyTok = getToken(); + if (yyTok == Tok_LeftBrace) { + namespaceDepths.push(namespaces.count()); + enterNamespace(&namespaces, ns); + yyTok = getToken(); + } else if (yyTok == Tok_Equals) { + // e.g. namespace Is = OuterSpace::InnerSpace; + QStringList fullName; + yyTok = getToken(); + if (yyTok == Tok_ColonColon) + fullName.append(QString()); + while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { + if (yyTok == Tok_Ident) + fullName.append(yyIdent); + yyTok = getToken(); + } + if (fullName.isEmpty()) + break; + NamespaceList nsl; + if (fullyQualify(namespaces, fullName, false, &nsl, 0)) { + modifyNamespace(&namespaces); + namespaces.last()->aliases.insert(ns, stringListifyNamespace(nsl)); + } + } + } else if (yyTok == Tok_LeftBrace) { + // Anonymous namespace + namespaceDepths.push(namespaces.count()); + yyTok = getToken(); + } + break; + case Tok_using: + yyTok = getToken(); + if (yyTok == Tok_namespace) { + QStringList fullName; + yyTok = getToken(); + if (yyTok == Tok_ColonColon) + fullName.append(QString()); + while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { + if (yyTok == Tok_Ident) + fullName.append(yyIdent); + yyTok = getToken(); + } + NamespaceList nsl; + QStringList unresolved; + if (fullyQualify(namespaces, fullName, false, &nsl, &unresolved)) { + modifyNamespace(&namespaces); + namespaces.last()->usings.insert(stringListifyNamespace(nsl)); + } + } else { + QStringList fullName; + if (yyTok == Tok_ColonColon) + fullName.append(QString()); + while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { + if (yyTok == Tok_Ident) + fullName.append(yyIdent); + yyTok = getToken(); + } + if (fullName.isEmpty()) + break; + NamespaceList nsl; + if (fullyQualify(namespaces, fullName, false, &nsl, 0)) { + modifyNamespace(&namespaces); + namespaces.last()->aliases.insert(nsl.last()->name, stringListifyNamespace(nsl)); + } + } + break; + case Tok_tr: + case Tok_trUtf8: + if (!results->tor) + goto case_default; + if (!sourcetext.isEmpty()) + qWarning("%s:%d: //%% cannot be used with tr() / QT_TR_NOOP(). Ignoring\n", + qPrintable(yyFileName), yyLineNo); + utf8 = (yyTok == Tok_trUtf8); + line = yyLineNo; + yyTok = getToken(); + if (match(Tok_LeftParen) && matchString(&text) && !text.isEmpty()) { + comment.clear(); + bool plural = false; + + if (match(Tok_RightParen)) { + // no comment + } else if (match(Tok_Comma) && matchStringOrNull(&comment)) { //comment + if (match(Tok_RightParen)) { + // ok, + } else if (match(Tok_Comma)) { + plural = true; + } + } + if (!pendingContext.isEmpty()) { + QStringList unresolved; + if (!fullyQualify(namespaces, pendingContext.split(strColons), true, + &functionContext, &unresolved)) { + functionContextUnresolved = unresolved.join(strColons); + qWarning("%s:%d: Qualifying with unknown namespace/class %s::%s\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(stringifyNamespace(functionContext)), + qPrintable(unresolved.first())); + } + pendingContext.clear(); + } + if (prefix.isEmpty()) { + if (functionContextUnresolved.isEmpty()) { + int idx = functionContext.length(); + if (idx < 2) { + qWarning("%s:%d: tr() cannot be called without context\n", + qPrintable(yyFileName), yyLineNo); + break; + } + while (!functionContext.at(idx - 1)->hasTrFunctions) { + if (idx == 1 || !functionContext.at(idx - 2)->isClass) { + idx = functionContext.length(); + if (!functionContext.last()->complained) { + qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(stringifyNamespace(functionContext))); + functionContext.last()->complained = true; + } + break; + } + --idx; + } + context.clear(); + for (int i = 1;;) { + context += functionContext.at(i)->name; + if (++i == idx) + break; + context += strColons; + } + } else { + context = (stringListifyNamespace(functionContext) + << functionContextUnresolved).join(strColons); + } + } else { +#ifdef DIAGNOSE_RETRANSLATABILITY + int last = prefix.lastIndexOf(strColons); + QString className = prefix.mid(last == -1 ? 0 : last + 2); + if (!className.isEmpty() && className == functionName) { + qWarning("%s::%d: It is not recommended to call tr() from within a constructor '%s::%s' ", + qPrintable(yyFileName), yyLineNo, + className.constData(), functionName.constData()); + } +#endif + prefix.chop(2); + NamespaceList nsl; + QStringList unresolved; + if (fullyQualify(functionContext, prefix.split(strColons), false, &nsl, &unresolved)) { + if (!nsl.last()->hasTrFunctions && !nsl.last()->complained) { + qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(stringifyNamespace(nsl))); + nsl.last()->complained = true; + } + context = stringifyNamespace(nsl); + } else { + context = (stringListifyNamespace(nsl) + unresolved).join(strColons); + } + prefix.clear(); + } + + recordMessage(line, context, text, comment, extracomment, msgid, extra, utf8, plural); + } + extracomment.clear(); + msgid.clear(); + extra.clear(); + break; + case Tok_translateUtf8: + case Tok_translate: + if (!results->tor) + goto case_default; + if (!sourcetext.isEmpty()) + qWarning("%s:%d: //%% cannot be used with translate() / QT_TRANSLATE_NOOP(). Ignoring\n", + qPrintable(yyFileName), yyLineNo); + utf8 = (yyTok == Tok_translateUtf8); + line = yyLineNo; + yyTok = getToken(); + if (match(Tok_LeftParen) + && matchString(&context) + && match(Tok_Comma) + && matchString(&text) && !text.isEmpty()) + { + comment.clear(); + bool plural = false; + if (!match(Tok_RightParen)) { + // look for comment + if (match(Tok_Comma) && matchStringOrNull(&comment)) { + if (!match(Tok_RightParen)) { + // look for encoding + if (match(Tok_Comma)) { + if (matchEncoding(&utf8)) { + if (!match(Tok_RightParen)) { + // look for the plural quantifier, + // this can be a number, an identifier or + // a function call, + // so for simplicity we mark it as plural if + // we know we have a comma instead of an + // right parentheses. + plural = match(Tok_Comma); + } + } else { + // This can be a QTranslator::translate("context", + // "source", "comment", n) plural translation + if (matchExpression() && match(Tok_RightParen)) { + plural = true; + } else { + break; + } + } + } else { + break; + } + } + } else { + break; + } + } + recordMessage(line, context, text, comment, extracomment, msgid, extra, utf8, plural); + } + extracomment.clear(); + msgid.clear(); + extra.clear(); + break; + case Tok_trid: + if (!results->tor) + goto case_default; + if (!sourcetext.isEmpty()) { + if (!msgid.isEmpty()) + qWarning("%s:%d: //= cannot be used with qtTrId() / QT_TRID_NOOP(). Ignoring\n", + qPrintable(yyFileName), yyLineNo); + //utf8 = false; // Maybe use //%% or something like that + line = yyLineNo; + yyTok = getToken(); + if (match(Tok_LeftParen) && matchString(&msgid) && !msgid.isEmpty()) { + bool plural = match(Tok_Comma); + recordMessage(line, QString(), sourcetext, QString(), extracomment, + msgid, extra, false, plural); + } + sourcetext.clear(); + } + extracomment.clear(); + msgid.clear(); + extra.clear(); + break; + case Tok_Q_DECLARE_TR_FUNCTIONS: + case Tok_Q_OBJECT: + namespaces.last()->hasTrFunctions = true; + yyTok = getToken(); + break; + case Tok_Ident: + prefix += yyIdent; + yyTok = getToken(); + if (yyTok != Tok_ColonColon) { + prefix.clear(); + if (yyTok == Tok_Ident && !yyParenDepth) + prospectiveContext.clear(); + } + break; + case Tok_Comment: + if (!results->tor) + goto case_default; + if (yyComment.startsWith(QLatin1Char(':'))) { + yyComment.remove(0, 1); + extracomment.append(yyComment); + } else if (yyComment.startsWith(QLatin1Char('='))) { + yyComment.remove(0, 1); + msgid = yyComment.simplified(); + } else if (yyComment.startsWith(QLatin1Char('~'))) { + yyComment.remove(0, 1); + yyComment = yyComment.trimmed(); + int k = yyComment.indexOf(QLatin1Char(' ')); + if (k > -1) + extra.insert(yyComment.left(k), yyComment.mid(k + 1).trimmed()); + } else if (yyComment.startsWith(QLatin1Char('%'))) { + int p = 1, c; + forever { + if (p >= yyComment.length()) + break; + c = yyComment.unicode()[p++].unicode(); + if (isspace(c)) + continue; + if (c != '"') { + qWarning("%s:%d: Unexpected character in meta string\n", + qPrintable(yyFileName), yyLineNo); + break; + } + forever { + if (p >= yyComment.length()) { + whoops: + qWarning("%s:%d: Unterminated meta string\n", + qPrintable(yyFileName), yyLineNo); + break; + } + c = yyComment.unicode()[p++].unicode(); + if (c == '"') + break; + if (c == '\\') { + if (p >= yyComment.length()) + goto whoops; + c = yyComment.unicode()[p++].unicode(); + if (c == '\n') + goto whoops; + sourcetext.append(QLatin1Char('\\')); + } + sourcetext.append(c); + } + } + } else { + comment = yyComment.simplified(); + if (comment.startsWith(QLatin1String(MagicComment))) { + comment.remove(0, sizeof(MagicComment) - 1); + int k = comment.indexOf(QLatin1Char(' ')); + if (k == -1) { + context = comment; + } else { + context = comment.left(k); + comment.remove(0, k + 1); + recordMessage(yyLineNo, context, QString(), comment, extracomment, + QString(), TranslatorMessage::ExtraData(), false, false); + extracomment.clear(); + results->tor->setExtras(extra); + extra.clear(); + } + } + } + yyTok = getToken(); + break; + case Tok_Arrow: + yyTok = getToken(); + if (yyTok == Tok_tr || yyTok == Tok_trUtf8) + qWarning("%s:%d: Cannot invoke tr() like this\n", + qPrintable(yyFileName), yyLineNo); + break; + case Tok_ColonColon: + if (yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0 && !yyTokColonSeen) + prospectiveContext = prefix; + prefix += strColons; + yyTok = getToken(); +#ifdef DIAGNOSE_RETRANSLATABILITY + if (yyTok == Tok_Ident && yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) + functionName = yyIdent; +#endif + break; + case Tok_RightBrace: + if (yyBraceDepth + 1 == namespaceDepths.count()) { + // class or namespace + Namespace *ns = namespaces.last(); + if (ns->needsTrFunctions && !ns->hasTrFunctions && !ns->complained) { + qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(stringifyNamespace(namespaces))); + ns->complained = true; + } + truncateNamespaces(&namespaces, namespaceDepths.pop()); + } + if (yyBraceDepth == namespaceDepths.count()) { + // function, class or namespace + if (!yyBraceDepth && !directInclude) { + truncateNamespaces(&functionContext, 1); + functionContextUnresolved = cd.m_defaultContext; + } else { + functionContext = namespaces; + functionContextUnresolved.clear(); + } + pendingContext.clear(); + } + // fallthrough + case Tok_Semicolon: + prospectiveContext.clear(); + prefix.clear(); + extracomment.clear(); + msgid.clear(); + extra.clear(); + yyTokColonSeen = false; + yyTok = getToken(); + break; + case Tok_Colon: + if (!prospectiveContext.isEmpty() + && yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) + pendingContext = prospectiveContext; + yyTokColonSeen = true; + yyTok = getToken(); + break; + case Tok_LeftBrace: + if (!prospectiveContext.isEmpty() + && yyBraceDepth == namespaceDepths.count() + 1 && yyParenDepth == 0) + pendingContext = prospectiveContext; + // fallthrough + case Tok_LeftParen: + case Tok_RightParen: + yyTokColonSeen = false; + yyTok = getToken(); + break; + default: + if (!yyParenDepth) + prospectiveContext.clear(); + case_default: + yyTok = getToken(); + break; + } + } + + if (yyBraceDepth != 0) + qWarning("%s:%d: Unbalanced opening brace in C++ code" + " (or abuse of the C++ preprocessor)\n", + qPrintable(yyFileName), yyBraceLineNo); + else if (yyParenDepth != 0) + qWarning("%s:%d: Unbalanced opening parenthesis in C++ code" + " (or abuse of the C++ preprocessor)\n", + qPrintable(yyFileName), yyParenLineNo); +} + +/* + Fetches tr() calls in C++ code in UI files (inside "<function>" + tag). This mechanism is obsolete. +*/ +void fetchtrInlinedCpp(const QString &in, Translator &translator, const QString &context) +{ + CppParser parser; + parser.setInput(in); + ConversionData cd; + QSet<QString> inclusions; + parser.setTranslator(&translator); + parser.parse(context, cd, inclusions); + parser.deleteResults(); +} + +void loadCPP(Translator &translator, const QStringList &filenames, ConversionData &cd) +{ + QByteArray codecName = cd.m_codecForSource.isEmpty() + ? translator.codecName() : cd.m_codecForSource; + QTextCodec *codec = QTextCodec::codecForName(codecName); + + foreach (const QString &filename, filenames) { + if (CppFiles::getResults(filename) || CppFiles::isBlacklisted(filename)) + continue; + + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + continue; + } + + CppParser parser; + QTextStream ts(&file); + ts.setCodec(codec); + ts.setAutoDetectUnicode(true); + if (ts.codec()->name() == "UTF-16") + translator.setCodecName("System"); + parser.setInput(ts, filename); + Translator *tor = new Translator; + tor->setCodecName(translator.codecName()); + parser.setTranslator(tor); + QSet<QString> inclusions; + parser.parse(cd.m_defaultContext, cd, inclusions); + CppFiles::setResults(filename, parser.getResults()); + } + + foreach (const QString filename, filenames) + if (!CppFiles::isBlacklisted(filename)) + if (Translator *tor = CppFiles::getResults(filename)->tor) + foreach (const TranslatorMessage &msg, tor->messages()) + translator.extend(msg); +} + +QT_END_NAMESPACE diff --git a/tools/linguist/lupdate/java.cpp b/tools/linguist/lupdate/java.cpp new file mode 100644 index 0000000..8a5d975 --- /dev/null +++ b/tools/linguist/lupdate/java.cpp @@ -0,0 +1,643 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Linguist of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lupdate.h" + +#include <translator.h> + +#include <QtCore/QDebug> +#include <QtCore/QFile> +#include <QtCore/QRegExp> +#include <QtCore/QStack> +#include <QtCore/QStack> +#include <QtCore/QString> +#include <QtCore/QTextCodec> + +#include <ctype.h> + +QT_BEGIN_NAMESPACE + +enum { Tok_Eof, Tok_class, Tok_return, Tok_tr, + Tok_translate, Tok_Ident, Tok_Package, + Tok_Comment, Tok_String, Tok_Colon, Tok_Dot, + Tok_LeftBrace, Tok_RightBrace, Tok_LeftParen, + Tok_RightParen, Tok_Comma, Tok_Semicolon, + Tok_Integer, Tok_Plus, Tok_PlusPlus, Tok_PlusEq, Tok_null }; + +class Scope +{ + public: + QString name; + enum Type {Clazz, Function, Other} type; + int line; + + Scope(const QString & name, Type type, int line) : + name(name), + type(type), + line(line) + {} + + ~Scope() + {} +}; + +/* + The tokenizer maintains the following global variables. The names + should be self-explanatory. +*/ + +static QString yyFileName; +static QChar yyCh; +static QString yyIdent; +static QString yyComment; +static QString yyString; + + +static qlonglong yyInteger; +static int yyParenDepth; +static int yyLineNo; +static int yyCurLineNo; +static int yyParenLineNo; +static int yyTok; + +// the string to read from and current position in the string +static QString yyInStr; +static int yyInPos; + +// The parser maintains the following global variables. +static QString yyPackage; +static QStack<Scope*> yyScope; +static QString yyDefaultContext; + +static QChar getChar() +{ + if (yyInPos >= yyInStr.size()) + return EOF; + QChar c = yyInStr[yyInPos++]; + if (c.unicode() == '\n') + ++yyCurLineNo; + return c.unicode(); +} + +static int getToken() +{ + const char tab[] = "bfnrt\"\'\\"; + const char backTab[] = "\b\f\n\r\t\"\'\\"; + + yyIdent.clear(); + yyComment.clear(); + yyString.clear(); + + while ( yyCh != EOF ) { + yyLineNo = yyCurLineNo; + + if ( yyCh.isLetter() || yyCh.toLatin1() == '_' ) { + do { + yyIdent.append(yyCh); + yyCh = getChar(); + } while ( yyCh.isLetterOrNumber() || yyCh.toLatin1() == '_' ); + + if (yyTok != Tok_Dot) { + switch ( yyIdent.at(0).toLatin1() ) { + case 'r': + if ( yyIdent == QLatin1String("return") ) + return Tok_return; + break; + case 'c': + if ( yyIdent == QLatin1String("class") ) + return Tok_class; + break; + case 'n': + if ( yyIdent == QLatin1String("null") ) + return Tok_null; + break; + } + } + switch ( yyIdent.at(0).toLatin1() ) { + case 'T': + // TR() for when all else fails + if ( yyIdent == QLatin1String("TR") ) + return Tok_tr; + break; + case 'p': + if( yyIdent == QLatin1String("package") ) + return Tok_Package; + break; + case 't': + if ( yyIdent == QLatin1String("tr") ) + return Tok_tr; + if ( yyIdent == QLatin1String("translate") ) + return Tok_translate; + } + return Tok_Ident; + } else { + switch ( yyCh.toLatin1() ) { + + case '/': + yyCh = getChar(); + if ( yyCh == QLatin1Char('/') ) { + do { + yyCh = getChar(); + if (yyCh == EOF) + break; + yyComment.append(yyCh); + } while (yyCh != QLatin1Char('\n')); + return Tok_Comment; + + } else if ( yyCh == QLatin1Char('*') ) { + bool metAster = false; + bool metAsterSlash = false; + + while ( !metAsterSlash ) { + yyCh = getChar(); + if ( yyCh == EOF ) { + qFatal( "%s: Unterminated Java comment starting at" + " line %d\n", + qPrintable(yyFileName), yyLineNo ); + + return Tok_Comment; + } + + yyComment.append( yyCh ); + + if ( yyCh == QLatin1Char('*') ) + metAster = true; + else if ( metAster && yyCh == QLatin1Char('/') ) + metAsterSlash = true; + else + metAster = false; + } + yyComment.chop(2); + yyCh = getChar(); + + return Tok_Comment; + } + break; + case '"': + yyCh = getChar(); + + while ( yyCh != EOF && yyCh != QLatin1Char('\n') && yyCh != QLatin1Char('"') ) { + if ( yyCh == QLatin1Char('\\') ) { + yyCh = getChar(); + if ( yyCh == QLatin1Char('u') ) { + yyCh = getChar(); + uint unicode(0); + for (int i = 4; i > 0; --i) { + unicode = unicode << 4; + if( yyCh.isDigit() ) { + unicode += yyCh.digitValue(); + } + else { + int sub(yyCh.toLower().toAscii() - 87); + if( sub > 15 || sub < 10) { + qFatal( "%s:%d: Invalid Unicode", + qPrintable(yyFileName), yyLineNo ); + } + unicode += sub; + } + yyCh = getChar(); + } + yyString.append(QChar(unicode)); + } + else if ( yyCh == QLatin1Char('\n') ) { + yyCh = getChar(); + } + else { + yyString.append( QLatin1Char(backTab[strchr( tab, yyCh.toAscii() ) - tab]) ); + yyCh = getChar(); + } + } else { + yyString.append(yyCh); + yyCh = getChar(); + } + } + + if ( yyCh != QLatin1Char('"') ) + qFatal( "%s:%d: Unterminated string", + qPrintable(yyFileName), yyLineNo ); + + yyCh = getChar(); + + return Tok_String; + + case ':': + yyCh = getChar(); + return Tok_Colon; + case '\'': + yyCh = getChar(); + + if ( yyCh == QLatin1Char('\\') ) + yyCh = getChar(); + do { + yyCh = getChar(); + } while ( yyCh != EOF && yyCh != QLatin1Char('\'') ); + yyCh = getChar(); + break; + case '{': + yyCh = getChar(); + return Tok_LeftBrace; + case '}': + yyCh = getChar(); + return Tok_RightBrace; + case '(': + if (yyParenDepth == 0) + yyParenLineNo = yyCurLineNo; + yyParenDepth++; + yyCh = getChar(); + return Tok_LeftParen; + case ')': + if (yyParenDepth == 0) + yyParenLineNo = yyCurLineNo; + yyParenDepth--; + yyCh = getChar(); + return Tok_RightParen; + case ',': + yyCh = getChar(); + return Tok_Comma; + case '.': + yyCh = getChar(); + return Tok_Dot; + case ';': + yyCh = getChar(); + return Tok_Semicolon; + case '+': + yyCh = getChar(); + if (yyCh == QLatin1Char('+')) { + yyCh = getChar(); + return Tok_PlusPlus; + } + if( yyCh == QLatin1Char('=') ){ + yyCh = getChar(); + return Tok_PlusEq; + } + return Tok_Plus; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + QByteArray ba; + ba += yyCh.toLatin1(); + yyCh = getChar(); + bool hex = yyCh == QLatin1Char('x'); + if ( hex ) { + ba += yyCh.toLatin1(); + yyCh = getChar(); + } + while ( hex ? isxdigit(yyCh.toLatin1()) : yyCh.isDigit() ) { + ba += yyCh.toLatin1(); + yyCh = getChar(); + } + bool ok; + yyInteger = ba.toLongLong(&ok); + if (ok) return Tok_Integer; + break; + } + default: + yyCh = getChar(); + } + } + } + return Tok_Eof; +} + +static bool match( int t ) +{ + bool matches = ( yyTok == t ); + if ( matches ) + yyTok = getToken(); + return matches; +} + +static bool matchString( QString &s ) +{ + if ( yyTok != Tok_String ) + return false; + + s = yyString; + yyTok = getToken(); + while ( yyTok == Tok_Plus ) { + yyTok = getToken(); + if (yyTok == Tok_String) + s += yyString; + else { + qWarning( "%s:%d: String used in translation can only contain strings" + " concatenated with other strings, not expressions or numbers.", + qPrintable(yyFileName), yyLineNo ); + return false; + } + yyTok = getToken(); + } + return true; +} + +static bool matchStringOrNull(QString &s) +{ + bool matches = matchString(s); + if (!matches) { + matches = (yyTok == Tok_null); + if (matches) + yyTok = getToken(); + } + return matches; +} + +/* + * match any expression that can return a number, which can be + * 1. Literal number (e.g. '11') + * 2. simple identifier (e.g. 'm_count') + * 3. simple function call (e.g. 'size()' ) + * 4. function call on an object (e.g. 'list.size()') + * 5. function call on an object (e.g. 'list->size()') + * + * Other cases: + * size(2,4) + * list().size() + * list(a,b).size(2,4) + * etc... + */ +static bool matchExpression() +{ + if (match(Tok_Integer)) { + return true; + } + + int parenlevel = 0; + while (match(Tok_Ident) || parenlevel > 0) { + if (yyTok == Tok_RightParen) { + if (parenlevel == 0) break; + --parenlevel; + yyTok = getToken(); + } else if (yyTok == Tok_LeftParen) { + yyTok = getToken(); + if (yyTok == Tok_RightParen) { + yyTok = getToken(); + } else { + ++parenlevel; + } + } else if (yyTok == Tok_Ident) { + continue; + } else if (parenlevel == 0) { + return false; + } + } + return true; +} + +static const QString context() +{ + QString context(yyPackage); + bool innerClass = false; + for (int i = 0; i < yyScope.size(); ++i) { + if (yyScope.at(i)->type == Scope::Clazz) { + if (innerClass) + context.append(QLatin1String("$")); + else + context.append(QLatin1String(".")); + + context.append(yyScope.at(i)->name); + innerClass = true; + } + } + return context.isEmpty() ? yyDefaultContext : context; +} + +static void recordMessage( + Translator *tor, const QString &context, const QString &text, const QString &comment, + const QString &extracomment, bool plural) +{ + TranslatorMessage msg( + context, text, comment, QString(), + yyFileName, yyLineNo, QStringList(), + TranslatorMessage::Unfinished, plural); + msg.setExtraComment(extracomment.simplified()); + tor->extend(msg); +} + +static void parse( Translator *tor ) +{ + QString text; + QString com; + QString extracomment; + + yyCh = getChar(); + + yyTok = getToken(); + while ( yyTok != Tok_Eof ) { + switch ( yyTok ) { + case Tok_class: + yyTok = getToken(); + if(yyTok == Tok_Ident) { + yyScope.push(new Scope(yyIdent, Scope::Clazz, yyLineNo)); + } + else { + qFatal( "%s:%d: Class must be followed by a classname", + qPrintable(yyFileName), yyLineNo ); + } + while (!match(Tok_LeftBrace)) { + yyTok = getToken(); + } + break; + + case Tok_tr: + yyTok = getToken(); + if ( match(Tok_LeftParen) && matchString(text) ) { + com.clear(); + bool plural = false; + + if ( match(Tok_RightParen) ) { + // no comment + } else if (match(Tok_Comma) && matchStringOrNull(com)) { //comment + if ( match(Tok_RightParen)) { + // ok, + } else if (match(Tok_Comma)) { + plural = true; + } + } + if (!text.isEmpty()) + recordMessage(tor, context(), text, com, extracomment, plural); + } + break; + case Tok_translate: + { + QString contextOverride; + yyTok = getToken(); + if ( match(Tok_LeftParen) && + matchString(contextOverride) && + match(Tok_Comma) && + matchString(text) ) { + + com.clear(); + bool plural = false; + if (!match(Tok_RightParen)) { + // look for comment + if ( match(Tok_Comma) && matchStringOrNull(com)) { + if (!match(Tok_RightParen)) { + if (match(Tok_Comma) && matchExpression() && match(Tok_RightParen)) { + plural = true; + } else { + break; + } + } + } else { + break; + } + } + if (!text.isEmpty()) + recordMessage(tor, contextOverride, text, com, extracomment, plural); + } + } + break; + + case Tok_Ident: + yyTok = getToken(); + break; + + case Tok_Comment: + if (yyComment.startsWith(QLatin1Char(':'))) { + yyComment.remove(0, 1); + extracomment.append(yyComment); + } + yyTok = getToken(); + break; + + case Tok_RightBrace: + if ( yyScope.isEmpty() ) { + qFatal( "%s:%d: Unbalanced right brace in Java code\n", + qPrintable(yyFileName), yyLineNo ); + } + else + delete (yyScope.pop()); + extracomment.clear(); + yyTok = getToken(); + break; + + case Tok_LeftBrace: + yyScope.push(new Scope(QString(), Scope::Other, yyLineNo)); + yyTok = getToken(); + break; + + case Tok_Semicolon: + extracomment.clear(); + yyTok = getToken(); + break; + + case Tok_Package: + yyTok = getToken(); + while(!match(Tok_Semicolon)) { + switch(yyTok) { + case Tok_Ident: + yyPackage.append(yyIdent); + break; + case Tok_Dot: + yyPackage.append(QLatin1String(".")); + break; + default: + qFatal( "%s:%d: Package keyword should be followed by com.package.name;", + qPrintable(yyFileName), yyLineNo ); + break; + } + yyTok = getToken(); + } + break; + + default: + yyTok = getToken(); + } + } + + if ( !yyScope.isEmpty() ) + qFatal( "%s:%d: Unbalanced braces in Java code\n", + qPrintable(yyFileName), yyScope.top()->line ); + else if ( yyParenDepth != 0 ) + qFatal( "%s:%d: Unbalanced parentheses in Java code\n", + qPrintable(yyFileName), yyParenLineNo ); +} + + +bool loadJava(Translator &translator, const QString &filename, ConversionData &cd) +{ + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + return false; + } + + yyDefaultContext = cd.m_defaultContext; + yyInPos = -1; + yyFileName = filename; + yyPackage.clear(); + yyScope.clear(); + yyTok = -1; + yyParenDepth = 0; + yyCurLineNo = 0; + yyParenLineNo = 1; + + QTextStream ts(&file); + QByteArray codecName; + if (!cd.m_codecForSource.isEmpty()) + codecName = cd.m_codecForSource; + else + codecName = translator.codecName(); // Just because it should be latin1 already + ts.setCodec(QTextCodec::codecForName(codecName)); + ts.setAutoDetectUnicode(true); + yyInStr = ts.readAll(); + yyInPos = 0; + yyFileName = filename; + yyCurLineNo = 1; + yyParenLineNo = 1; + yyCh = getChar(); + + parse(&translator); + + // Java uses UTF-16 internally and Jambi makes UTF-8 for tr() purposes of it. + translator.setCodecName("UTF-8"); + return true; +} + +QT_END_NAMESPACE diff --git a/tools/linguist/lupdate/lupdate.1 b/tools/linguist/lupdate/lupdate.1 index 68958b9..b37e7b3 100644 --- a/tools/linguist/lupdate/lupdate.1 +++ b/tools/linguist/lupdate/lupdate.1 @@ -52,12 +52,12 @@ tool for the Qt GUI toolkit. .B Lupdate reads a qmake/tmake project file (.pro file), finds the translatable strings in the specified source, header and interface files, and -updates the translation files (.ts files) specified in it. The +updates the translation files (TS files) specified in it. The translation files are given to the translator who uses .B Qt Linguist to read the files and insert the translations. .PP -The .ts file format is a simple human-readable XML format that can be +The TS file format is a simple human-readable XML format that can be used with version control systems if required. .PP .SH OPTIONS @@ -74,7 +74,7 @@ Default: 'ui,c,c++,cc,cpp,cxx,ch,h,h++,hh,hpp,hxx'. Display the usage and exit. .TP .I "-locations {absolute|relative|none}" -Specify/override how source code references are saved in ts files. +Specify/override how source code references are saved in TS files. Default is absolute. .TP .I "-no-obsolete" @@ -84,7 +84,7 @@ Drop all obsolete strings. Do not recursively scan the following directories. .TP .I "-no-sort" -Do not sort contexts in .ts files. +Do not sort contexts in TS files. .TP .I "-pluralonly" Only include plural form messages. @@ -97,7 +97,7 @@ file syntax but different file suffix Recursively scan the following directories. .TP .I "-silent" -Don't explain what is being done. +Do not explain what is being done. .TP .I "-source-language <language>[_<region>]" Specify/override the language of the source strings. Defaults to @@ -139,8 +139,8 @@ translations will be reused as far as possible, and translated strings that have vanished from the source files are marked obsolete. .PP .B lupdate -can also be invoked with a list of C++ source files, .ui files -and .ts files: +can also be invoked with a list of C++ source files, UI files +and TS files: .PP .in +4 .nf diff --git a/tools/linguist/lupdate/lupdate.h b/tools/linguist/lupdate/lupdate.h new file mode 100644 index 0000000..7e28cf5 --- /dev/null +++ b/tools/linguist/lupdate/lupdate.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Linguist of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LUPDATE_H +#define LUPDATE_H + +#include "qglobal.h" + +#include <QList> + +QT_BEGIN_NAMESPACE + +class ConversionData; +class QString; +class QStringList; +class Translator; +class TranslatorMessage; + +enum UpdateOption { + Verbose = 1, + NoObsolete = 2, + PluralOnly = 4, + NoSort = 8, + HeuristicSameText = 16, + HeuristicSimilarText = 32, + HeuristicNumber = 64, + AbsoluteLocations = 256, + RelativeLocations = 512, + NoLocations = 1024, + NoUiLines = 2048 +}; + +Q_DECLARE_FLAGS(UpdateOptions, UpdateOption) +Q_DECLARE_OPERATORS_FOR_FLAGS(UpdateOptions) + +Translator merge(const Translator &tor, const Translator &virginTor, + UpdateOptions options, QString &err); + +void fetchtrInlinedCpp(const QString &in, Translator &translator, const QString &context); +void loadCPP(Translator &translator, const QStringList &filenames, ConversionData &cd); +bool loadJava(Translator &translator, const QString &filename, ConversionData &cd); +bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd); +bool loadUI(Translator &translator, const QString &filename, ConversionData &cd); + +QT_END_NAMESPACE + +#endif diff --git a/tools/linguist/lupdate/lupdate.pro b/tools/linguist/lupdate/lupdate.pro index b05a4ef..ccc2d47 100644 --- a/tools/linguist/lupdate/lupdate.pro +++ b/tools/linguist/lupdate/lupdate.pro @@ -14,9 +14,20 @@ build_all:!build_pass { include(../shared/formats.pri) include(../shared/proparser.pri) -include(../shared/translatortools.pri) -SOURCES += main.cpp +SOURCES += \ + main.cpp \ + merge.cpp \ + ../shared/simtexth.cpp \ + \ + cpp.cpp \ + java.cpp \ + qscript.cpp \ + ui.cpp + +HEADERS += \ + lupdate.h \ + ../shared/simtexth.h DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII diff --git a/tools/linguist/lupdate/main.cpp b/tools/linguist/lupdate/main.cpp index 6b454ef..5a26774 100644 --- a/tools/linguist/lupdate/main.cpp +++ b/tools/linguist/lupdate/main.cpp @@ -39,10 +39,11 @@ ** ****************************************************************************/ -#include "translator.h" -#include "translatortools.h" -#include "profileevaluator.h" -#include "proreader.h" +#include "lupdate.h" + +#include <translator.h> +#include <profileevaluator.h> +#include <proreader.h> #include <QtCore/QCoreApplication> #include <QtCore/QDebug> @@ -84,12 +85,12 @@ static void printUsage() { printOut(QObject::tr( "Usage:\n" - " lupdate [options] [project-file]\n" + " lupdate [options] [project-file]...\n" " lupdate [options] [source-file|path]... -ts ts-files\n\n" - "lupdate is part of Qt's Linguist tool chain. It can be used as a\n" - "stand-alone tool to create XML based translations files in the .ts\n" - "format from translatable messages in C++ and Java source code.\n\n" - "lupdate can also merge such messages into existing .ts files.\n\n" + "lupdate is part of Qt's Linguist tool chain. It extracts translatable\n" + "messages from Qt UI files, C++, Java and JavaScript/QtScript source code.\n" + "Extracted messages are stored in textual translation source files (typically\n" + "Qt TS XML). New and modified messages can be merged into existing TS files.\n\n" "Options:\n" " -help Display this information and exit.\n" " -no-obsolete\n" @@ -103,16 +104,19 @@ static void printUsage() " -silent\n" " Do not explain what is being done.\n" " -no-sort\n" - " Do not sort contexts in .ts files.\n" + " Do not sort contexts in TS files.\n" " -no-recursive\n" " Do not recursively scan the following directories.\n" " -recursive\n" - " Recursively scan the following directories.\n" + " Recursively scan the following directories (default).\n" + " -I <includepath> or -I<includepath>\n" + " Additional location to look for include files.\n" + " May be specified multiple times.\n" " -locations {absolute|relative|none}\n" - " Specify/override how source code references are saved in ts files.\n" + " Specify/override how source code references are saved in TS files.\n" " Default is absolute.\n" " -no-ui-lines\n" - " Do not record line numbers in references to .ui files.\n" + " Do not record line numbers in references to UI files.\n" " -disable-heuristic {sametext|similartext|number}\n" " Disable the named merge heuristic. Can be specified multiple times.\n" " -pro <filename>\n" @@ -223,7 +227,10 @@ int main(int argc, char **argv) QByteArray codecForSource; QStringList tsFileNames; QStringList proFiles; + QMultiHash<QString, QString> allCSources; + QSet<QString> projectRoots; QStringList sourceFiles; + QStringList includePath; QString targetLanguage; QString sourceLanguage; @@ -350,6 +357,18 @@ int main(int argc, char **argv) proFiles += args[i]; numFiles++; continue; + } else if (arg.startsWith(QLatin1String("-I"))) { + if (arg.length() == 2) { + ++i; + if (i == argc) { + qWarning("The -I option should be followed by a path."); + return 1; + } + includePath += args[i]; + } else { + includePath += args[i].mid(2); + } + continue; } else if (arg.startsWith(QLatin1String("-")) && arg != QLatin1String("-")) { qWarning("Unrecognized option '%s'", qPrintable(arg)); return 1; @@ -394,33 +413,46 @@ int main(int argc, char **argv) if (options & Verbose) printOut(QObject::tr("Scanning directory '%1'...").arg(arg)); QDir dir = QDir(fi.filePath()); + projectRoots.insert(dir.absolutePath() + QLatin1Char('/')); if (extensionsNameFilters.isEmpty()) { - extensions = extensions.trimmed(); - // Remove the potential dot in front of each extension - if (extensions.startsWith(QLatin1Char('.'))) - extensions.remove(0,1); - extensions.replace(QLatin1String(",."), QLatin1String(",")); - - extensions.insert(0, QLatin1String("*.")); - extensions.replace(QLatin1Char(','), QLatin1String(",*.")); - extensionsNameFilters = extensions.split(QLatin1Char(',')); + foreach (QString ext, extensions.split(QLatin1Char(','))) { + ext = ext.trimmed(); + if (ext.startsWith(QLatin1Char('.'))) + ext.remove(0,1); + ext.insert(0, QLatin1String("*.")); + extensionsNameFilters << ext; + } } QDir::Filters filters = QDir::Files | QDir::NoSymLinks; QFileInfoList fileinfolist; recursiveFileInfoList(dir, extensionsNameFilters, filters, recursiveScan, &fileinfolist); - QFileInfoList::iterator ii; - QString fn; - for (ii = fileinfolist.begin(); ii != fileinfolist.end(); ++ii) { - // Make sure the path separator is stored with '/' in the ts file - sourceFiles << ii->canonicalFilePath().replace(QLatin1Char('\\'), QLatin1Char('/')); + int scanRootLen = dir.absolutePath().length(); + foreach (const QFileInfo &fi, fileinfolist) { + QString fn = QDir::cleanPath(fi.absoluteFilePath()); + sourceFiles << fn; + + if (!fn.endsWith(QLatin1String(".java")) + && !fn.endsWith(QLatin1String(".ui")) + && !fn.endsWith(QLatin1String(".js")) + && !fn.endsWith(QLatin1String(".qs"))) { + int offset = 0; + int depth = 0; + do { + offset = fn.lastIndexOf(QLatin1Char('/'), offset - 1); + QString ffn = fn.mid(offset + 1); + allCSources.insert(ffn, fn); + } while (++depth < 3 && offset > scanRootLen); + } } } else { - sourceFiles << fi.canonicalFilePath().replace(QLatin1Char('\\'), QLatin1Char('/')); + sourceFiles << QDir::cleanPath(fi.absoluteFilePath());; } } } // for args + foreach (const QString &proFile, proFiles) + projectRoots.insert(QDir::cleanPath(QFileInfo(proFile).absolutePath()) + QLatin1Char('/')); bool firstPass = true; bool fail = false; @@ -428,6 +460,9 @@ int main(int argc, char **argv) ConversionData cd; cd.m_defaultContext = defaultContext; cd.m_noUiLines = options & NoUiLines; + cd.m_projectRoots = projectRoots; + cd.m_includePath = includePath; + cd.m_allCSources = allCSources; QStringList tsFiles = tsFileNames; if (proFiles.count() > 0) { @@ -457,6 +492,8 @@ int main(int argc, char **argv) continue; } + cd.m_includePath += visitor.values(QLatin1String("INCLUDEPATH")); + evaluateProFile(visitor, &variables, pfi.absolutePath()); sourceFiles = variables.value("SOURCES"); @@ -479,30 +516,20 @@ int main(int argc, char **argv) tsFiles += variables.value("TRANSLATIONS"); } + QStringList sourceFilesCpp; for (QStringList::iterator it = sourceFiles.begin(); it != sourceFiles.end(); ++it) { - if (it->endsWith(QLatin1String(".java"), Qt::CaseInsensitive)) { - cd.m_sourceFileName = *it; - fetchedTor.load(*it, cd, QLatin1String("java")); - //fetchtr_java(*it, &fetchedTor, defaultContext, true, codecForSource); - } - else if (it->endsWith(QLatin1String(".jui"), Qt::CaseInsensitive)) { - fetchedTor.load(*it, cd, QLatin1String("jui")); - } - else if (it->endsWith(QLatin1String(".ui"), Qt::CaseInsensitive)) { - fetchedTor.load(*it, cd, QLatin1String("ui")); - //fetchedTor.load(*it + QLatin1String(".h"), cd, QLatin1String("cpp")); - //fetchtr_ui(*it, &fetchedTor, defaultContext, true); - //fetchtr_cpp(*it + QLatin1String(".h"), &fetchedTor, - // defaultContext, false, codecForSource); - } + if (it->endsWith(QLatin1String(".java"), Qt::CaseInsensitive)) + loadJava(fetchedTor, *it, cd); + else if (it->endsWith(QLatin1String(".ui"), Qt::CaseInsensitive) + || it->endsWith(QLatin1String(".jui"), Qt::CaseInsensitive)) + loadUI(fetchedTor, *it, cd); else if (it->endsWith(QLatin1String(".js"), Qt::CaseInsensitive) - || it->endsWith(QLatin1String(".qs"), Qt::CaseInsensitive)) { - fetchedTor.load(*it, cd, QLatin1String("js")); - } else { - fetchedTor.load(*it, cd, QLatin1String("cpp")); - //fetchtr_cpp(*it, &fetchedTor, defaultContext, true, codecForSource); - } + || it->endsWith(QLatin1String(".qs"), Qt::CaseInsensitive)) + loadQScript(fetchedTor, *it, cd); + else + sourceFilesCpp << *it; } + loadCPP(fetchedTor, sourceFilesCpp, cd); if (!cd.error().isEmpty()) printOut(cd.error()); diff --git a/tools/linguist/lupdate/merge.cpp b/tools/linguist/lupdate/merge.cpp new file mode 100644 index 0000000..7925fb2 --- /dev/null +++ b/tools/linguist/lupdate/merge.cpp @@ -0,0 +1,505 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Linguist of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lupdate.h" + +#include "simtexth.h" +#include "translator.h" + +#include <QtCore/QDebug> +#include <QtCore/QMap> +#include <QtCore/QStringList> +#include <QtCore/QTextCodec> +#include <QtCore/QVector> + +typedef QList<TranslatorMessage> TML; +typedef QMap<QString, TranslatorMessage> TMM; + + +QT_BEGIN_NAMESPACE + +static bool isDigitFriendly(QChar c) +{ + return c.isPunct() || c.isSpace(); +} + +static int numberLength(const QString &s, int i) +{ + if (i < s.size() || !s.at(i).isDigit()) + return 0; + + int pos = i; + do { + ++i; + } while (i < s.size() + && (s.at(i).isDigit() + || (isDigitFriendly(s[i]) + && i + 1 < s.size() + && (s[i + 1].isDigit() + || (isDigitFriendly(s[i + 1]) + && i + 2 < s.size() + && s[i + 2].isDigit()))))); + return i - pos; +} + + +/* + Returns a version of 'key' where all numbers have been replaced by zeroes. If + there were none, returns "". +*/ +static QString zeroKey(const QString &key) +{ + QString zeroed; + bool metSomething = false; + + for (int i = 0; i != key.size(); ++i) { + int len = numberLength(key, i); + if (len > 0) { + i += len; + zeroed.append(QLatin1Char('0')); + metSomething = true; + } else { + zeroed.append(key.at(i)); + } + } + return metSomething ? zeroed : QString(); +} + +static QString translationAttempt(const QString &oldTranslation, + const QString &oldSource, const QString &newSource) +{ + int p = zeroKey(oldSource).count(QLatin1Char('0')); + QString attempt; + QStringList oldNumbers; + QStringList newNumbers; + QVector<bool> met(p); + QVector<int> matchedYet(p); + int i, j; + int k = 0, ell, best; + int m, n; + int pass; + + /* + This algorithm is hard to follow, so we'll consider an example + all along: oldTranslation is "XeT 3.0", oldSource is "TeX 3.0" + and newSource is "XeT 3.1". + + First, we set up two tables: oldNumbers and newNumbers. In our + example, oldNumber[0] is "3.0" and newNumber[0] is "3.1". + */ + for (i = 0, j = 0; i < oldSource.size(); i++, j++) { + m = numberLength(oldSource, i); + n = numberLength(newSource, j); + if (m > 0) { + oldNumbers.append(oldSource.mid(i, m + 1)); + newNumbers.append(newSource.mid(j, n + 1)); + i += m; + j += n; + met[k] = false; + matchedYet[k] = 0; + k++; + } + } + + /* + We now go over the old translation, "XeT 3.0", one letter at a + time, looking for numbers found in oldNumbers. Whenever such a + number is met, it is replaced with its newNumber equivalent. In + our example, the "3.0" of "XeT 3.0" becomes "3.1". + */ + for (i = 0; i < oldTranslation.length(); i++) { + attempt += oldTranslation[i]; + for (k = 0; k < p; k++) { + if (oldTranslation[i] == oldNumbers[k][matchedYet[k]]) + matchedYet[k]++; + else + matchedYet[k] = 0; + } + + /* + Let's find out if the last character ended a match. We make + two passes over the data. In the first pass, we try to + match only numbers that weren't matched yet; if that fails, + the second pass does the trick. This is useful in some + suspicious cases, flagged below. + */ + for (pass = 0; pass < 2; pass++) { + best = p; // an impossible value + for (k = 0; k < p; k++) { + if ((!met[k] || pass > 0) && + matchedYet[k] == oldNumbers[k].length() && + numberLength(oldTranslation, i + 1 - matchedYet[k]) == matchedYet[k]) { + // the longer the better + if (best == p || matchedYet[k] > matchedYet[best]) + best = k; + } + } + if (best != p) { + attempt.truncate(attempt.length() - matchedYet[best]); + attempt += newNumbers[best]; + met[best] = true; + for (k = 0; k < p; k++) + matchedYet[k] = 0; + break; + } + } + } + + /* + We flag two kinds of suspicious cases. They are identified as + such with comments such as "{2000?}" at the end. + + Example of the first kind: old source text "TeX 3.0" translated + as "XeT 2.0" is flagged "TeX 2.0 {3.0?}", no matter what the + new text is. + */ + for (k = 0; k < p; k++) { + if (!met[k]) + attempt += QLatin1String(" {") + newNumbers[k] + QLatin1String("?}"); + } + + /* + Example of the second kind: "1 of 1" translated as "1 af 1", + with new source text "1 of 2", generates "1 af 2 {1 or 2?}" + because it's not clear which of "1 af 2" and "2 af 1" is right. + */ + for (k = 0; k < p; k++) { + for (ell = 0; ell < p; ell++) { + if (k != ell && oldNumbers[k] == oldNumbers[ell] && + newNumbers[k] < newNumbers[ell]) + attempt += QLatin1String(" {") + newNumbers[k] + QLatin1String(" or ") + + newNumbers[ell] + QLatin1String("?}"); + } + } + return attempt; +} + + +/* + Augments a Translator with translations easily derived from + similar existing (probably obsolete) translations. + + For example, if "TeX 3.0" is translated as "XeT 3.0" and "TeX 3.1" + has no translation, "XeT 3.1" is added to the translator and is + marked Unfinished. + + Returns the number of additional messages that this heuristic translated. +*/ +int applyNumberHeuristic(Translator &tor) +{ + TMM translated, untranslated; + TMM::Iterator t, u; + TML all = tor.messages(); + TML::Iterator it; + int inserted = 0; + + for (it = all.begin(); it != all.end(); ++it) { + bool hasTranslation = it->isTranslated(); + if (it->type() == TranslatorMessage::Unfinished) { + if (!hasTranslation) + untranslated.insert(it->context() + QLatin1Char('\n') + + it->sourceText() + QLatin1Char('\n') + + it->comment(), *it); + } else if (hasTranslation && it->translations().count() == 1) { + translated.insert(zeroKey(it->sourceText()), *it); + } + } + + for (u = untranslated.begin(); u != untranslated.end(); ++u) { + t = translated.find(zeroKey((*u).sourceText())); + if (t != translated.end() && !t.key().isEmpty() + && t->sourceText() != u->sourceText()) { + TranslatorMessage m = *u; + m.setTranslation(translationAttempt(t->translation(), t->sourceText(), + u->sourceText())); + tor.replace(m); + inserted++; + } + } + return inserted; +} + + +/* + Augments a Translator with trivially derived translations. + + For example, if "Enabled:" is consistendly translated as "Eingeschaltet:" no + matter the context or the comment, "Eingeschaltet:" is added as the + translation of any untranslated "Enabled:" text and is marked Unfinished. + + Returns the number of additional messages that this heuristic translated. +*/ + +int applySameTextHeuristic(Translator &tor) +{ + TMM translated; + TMM avoid; + TMM::Iterator t; + TML untranslated; + TML::Iterator u; + TML all = tor.messages(); + TML::Iterator it; + int inserted = 0; + + for (it = all.begin(); it != all.end(); ++it) { + if (!it->isTranslated()) { + if (it->type() == TranslatorMessage::Unfinished) + untranslated.append(*it); + } else { + QString key = it->sourceText(); + t = translated.find(key); + if (t != translated.end()) { + /* + The same source text is translated at least two + different ways. Do nothing then. + */ + if (t->translations() != it->translations()) { + translated.remove(key); + avoid.insert(key, *it); + } + } else if (!avoid.contains(key)) { + translated.insert(key, *it); + } + } + } + + for (u = untranslated.begin(); u != untranslated.end(); ++u) { + QString key = u->sourceText(); + t = translated.find(key); + if (t != translated.end()) { + TranslatorMessage m = *u; + m.setTranslations(t->translations()); + tor.replace(m); + ++inserted; + } + } + return inserted; +} + + + +/* + Merges two Translator objects. The first one + is a set of source texts and translations for a previous version of + the internationalized program; the second one is a set of fresh + source texts newly extracted from the source code, without any + translation yet. +*/ + +Translator merge(const Translator &tor, const Translator &virginTor, + UpdateOptions options, QString &err) +{ + int known = 0; + int neww = 0; + int obsoleted = 0; + int similarTextHeuristicCount = 0; + + Translator outTor; + outTor.setLanguageCode(tor.languageCode()); + outTor.setSourceLanguageCode(tor.sourceLanguageCode()); + outTor.setLocationsType(tor.locationsType()); + outTor.setCodecName(tor.codecName()); + + /* + The types of all the messages from the vernacular translator + are updated according to the virgin translator. + */ + foreach (TranslatorMessage m, tor.messages()) { + TranslatorMessage::Type newType = TranslatorMessage::Finished; + + if (m.sourceText().isEmpty()) { + // context/file comment + TranslatorMessage mv = virginTor.find(m.context()); + if (!mv.isNull()) + m.setComment(mv.comment()); + } else { + TranslatorMessage mv = virginTor.find(m.context(), m.sourceText(), m.comment()); + if (mv.isNull()) { + if (!(options & HeuristicSimilarText)) { + newType = TranslatorMessage::Obsolete; + if (m.type() != TranslatorMessage::Obsolete) + obsoleted++; + m.clearReferences(); + } else { + mv = virginTor.find(m.context(), m.comment(), m.allReferences()); + if (mv.isNull()) { + // did not find it in the virgin, mark it as obsolete + newType = TranslatorMessage::Obsolete; + if (m.type() != TranslatorMessage::Obsolete) + obsoleted++; + m.clearReferences(); + } else { + // Do not just accept it if its on the same line number, + // but different source text. + // Also check if the texts are more or less similar before + // we consider them to represent the same message... + if (getSimilarityScore(m.sourceText(), mv.sourceText()) >= textSimilarityThreshold) { + // It is just slightly modified, assume that it is the same string + + // Mark it as unfinished. (Since the source text + // was changed it might require re-translating...) + newType = TranslatorMessage::Unfinished; + ++similarTextHeuristicCount; + neww++; + + m.setOldSourceText(m.sourceText()); + m.setSourceText(mv.sourceText()); + const QString &oldpluralsource = m.extra(QLatin1String("po-msgid_plural")); + if (!oldpluralsource.isEmpty()) { + m.setExtra(QLatin1String("po-old_msgid_plural"), oldpluralsource); + m.unsetExtra(QLatin1String("po-msgid_plural")); + } + m.setReferences(mv.allReferences()); // Update secondary references + m.setPlural(mv.isPlural()); + m.setUtf8(mv.isUtf8()); + m.setExtraComment(mv.extraComment()); + } else { + // The virgin and vernacular sourceTexts are so + // different that we could not find it. + newType = TranslatorMessage::Obsolete; + if (m.type() != TranslatorMessage::Obsolete) + obsoleted++; + m.clearReferences(); + } + } + } + } else { + switch (m.type()) { + case TranslatorMessage::Finished: + default: + if (m.isPlural() == mv.isPlural()) { + newType = TranslatorMessage::Finished; + } else { + newType = TranslatorMessage::Unfinished; + } + known++; + break; + case TranslatorMessage::Unfinished: + newType = TranslatorMessage::Unfinished; + known++; + break; + case TranslatorMessage::Obsolete: + newType = TranslatorMessage::Unfinished; + neww++; + } + + // Always get the filename and linenumber info from the + // virgin Translator, in case it has changed location. + // This should also enable us to read a file that does not + // have the <location> element. + // why not use operator=()? Because it overwrites e.g. userData. + m.setReferences(mv.allReferences()); + m.setPlural(mv.isPlural()); + m.setUtf8(mv.isUtf8()); + m.setExtraComment(mv.extraComment()); + } + } + + m.setType(newType); + outTor.append(m); + } + + /* + Messages found only in the virgin translator are added to the + vernacular translator. + */ + foreach (const TranslatorMessage &mv, virginTor.messages()) { + if (mv.sourceText().isEmpty()) { + if (tor.contains(mv.context())) + continue; + } else { + if (tor.contains(mv.context(), mv.sourceText(), mv.comment())) + continue; + if (options & HeuristicSimilarText) { + TranslatorMessage m = tor.find(mv.context(), mv.comment(), mv.allReferences()); + if (!m.isNull()) { + if (getSimilarityScore(m.sourceText(), mv.sourceText()) >= textSimilarityThreshold) + continue; + } + } + } + if (options & NoLocations) + outTor.append(mv); + else + outTor.appendSorted(mv); + if (!mv.sourceText().isEmpty()) + ++neww; + } + + /* + The same-text heuristic handles cases where a message has an + obsolete counterpart with a different context or comment. + */ + int sameTextHeuristicCount = (options & HeuristicSameText) ? applySameTextHeuristic(outTor) : 0; + + /* + The number heuristic handles cases where a message has an + obsolete counterpart with mostly numbers differing in the + source text. + */ + int sameNumberHeuristicCount = (options & HeuristicNumber) ? applyNumberHeuristic(outTor) : 0; + + if (options & Verbose) { + int totalFound = neww + known; + err += QObject::tr(" Found %n source text(s) (%1 new and %2 already existing)\n", 0, totalFound).arg(neww).arg(known); + + if (obsoleted) { + if (options & NoObsolete) { + err += QObject::tr(" Removed %n obsolete entries\n", 0, obsoleted); + } else { + err += QObject::tr(" Kept %n obsolete entries\n", 0, obsoleted); + } + } + + if (sameNumberHeuristicCount) + err += QObject::tr(" Number heuristic provided %n translation(s)\n", + 0, sameNumberHeuristicCount); + if (sameTextHeuristicCount) + err += QObject::tr(" Same-text heuristic provided %n translation(s)\n", + 0, sameTextHeuristicCount); + if (similarTextHeuristicCount) + err += QObject::tr(" Similar-text heuristic provided %n translation(s)\n", + 0, similarTextHeuristicCount); + } + return outTor; +} + +QT_END_NAMESPACE diff --git a/tools/linguist/lupdate/qscript.cpp b/tools/linguist/lupdate/qscript.cpp new file mode 100644 index 0000000..7a701ae --- /dev/null +++ b/tools/linguist/lupdate/qscript.cpp @@ -0,0 +1,2391 @@ +// This file was generated by qlalr - DO NOT EDIT! +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Linguist of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +class QScriptGrammar +{ +public: + enum { + EOF_SYMBOL = 0, + T_AND = 1, + T_AND_AND = 2, + T_AND_EQ = 3, + T_AUTOMATIC_SEMICOLON = 62, + T_BREAK = 4, + T_CASE = 5, + T_CATCH = 6, + T_COLON = 7, + T_COMMA = 8, + T_CONST = 81, + T_CONTINUE = 9, + T_DEBUGGER = 82, + T_DEFAULT = 10, + T_DELETE = 11, + T_DIVIDE_ = 12, + T_DIVIDE_EQ = 13, + T_DO = 14, + T_DOT = 15, + T_ELSE = 16, + T_EQ = 17, + T_EQ_EQ = 18, + T_EQ_EQ_EQ = 19, + T_FALSE = 80, + T_FINALLY = 20, + T_FOR = 21, + T_FUNCTION = 22, + T_GE = 23, + T_GT = 24, + T_GT_GT = 25, + T_GT_GT_EQ = 26, + T_GT_GT_GT = 27, + T_GT_GT_GT_EQ = 28, + T_IDENTIFIER = 29, + T_IF = 30, + T_IN = 31, + T_INSTANCEOF = 32, + T_LBRACE = 33, + T_LBRACKET = 34, + T_LE = 35, + T_LPAREN = 36, + T_LT = 37, + T_LT_LT = 38, + T_LT_LT_EQ = 39, + T_MINUS = 40, + T_MINUS_EQ = 41, + T_MINUS_MINUS = 42, + T_NEW = 43, + T_NOT = 44, + T_NOT_EQ = 45, + T_NOT_EQ_EQ = 46, + T_NULL = 78, + T_NUMERIC_LITERAL = 47, + T_OR = 48, + T_OR_EQ = 49, + T_OR_OR = 50, + T_PLUS = 51, + T_PLUS_EQ = 52, + T_PLUS_PLUS = 53, + T_QUESTION = 54, + T_RBRACE = 55, + T_RBRACKET = 56, + T_REMAINDER = 57, + T_REMAINDER_EQ = 58, + T_RESERVED_WORD = 83, + T_RETURN = 59, + T_RPAREN = 60, + T_SEMICOLON = 61, + T_STAR = 63, + T_STAR_EQ = 64, + T_STRING_LITERAL = 65, + T_SWITCH = 66, + T_THIS = 67, + T_THROW = 68, + T_TILDE = 69, + T_TRUE = 79, + T_TRY = 70, + T_TYPEOF = 71, + T_VAR = 72, + T_VOID = 73, + T_WHILE = 74, + T_WITH = 75, + T_XOR = 76, + T_XOR_EQ = 77, + + ACCEPT_STATE = 236, + RULE_COUNT = 267, + STATE_COUNT = 465, + TERMINAL_COUNT = 84, + NON_TERMINAL_COUNT = 88, + + GOTO_INDEX_OFFSET = 465, + GOTO_INFO_OFFSET = 1374, + GOTO_CHECK_OFFSET = 1374 + }; + + static const char *const spell []; + static const int lhs []; + static const int rhs []; + static const int goto_default []; + static const int action_default []; + static const int action_index []; + static const int action_info []; + static const int action_check []; + + static inline int nt_action (int state, int nt) + { + const int *const goto_index = &action_index [GOTO_INDEX_OFFSET]; + const int *const goto_check = &action_check [GOTO_CHECK_OFFSET]; + + const int yyn = goto_index [state] + nt; + + if (yyn < 0 || goto_check [yyn] != nt) + return goto_default [nt]; + + const int *const goto_info = &action_info [GOTO_INFO_OFFSET]; + return goto_info [yyn]; + } + + static inline int t_action (int state, int token) + { + const int yyn = action_index [state] + token; + + if (yyn < 0 || action_check [yyn] != token) + return - action_default [state]; + + return action_info [yyn]; + } +}; + + +const char *const QScriptGrammar::spell [] = { + "end of file", "&", "&&", "&=", "break", "case", "catch", ":", ";", "continue", + "default", "delete", "/", "/=", "do", ".", "else", "=", "==", "===", + "finally", "for", "function", ">=", ">", ">>", ">>=", ">>>", ">>>=", "identifier", + "if", "in", "instanceof", "{", "[", "<=", "(", "<", "<<", "<<=", + "-", "-=", "--", "new", "!", "!=", "!==", "numeric literal", "|", "|=", + "||", "+", "+=", "++", "?", "}", "]", "%", "%=", "return", + ")", ";", 0, "*", "*=", "string literal", "switch", "this", "throw", "~", + "try", "typeof", "var", "void", "while", "with", "^", "^=", "null", "true", + "false", "const", "debugger", "reserved word"}; + +const int QScriptGrammar::lhs [] = { + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 87, 87, 91, 91, 86, 86, + 92, 92, 93, 93, 93, 93, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 95, 95, 96, + 96, 96, 96, 96, 99, 99, 100, 100, 100, 100, + 98, 98, 101, 101, 102, 102, 103, 103, 103, 104, + 104, 104, 104, 104, 104, 104, 104, 104, 104, 105, + 105, 105, 105, 106, 106, 106, 107, 107, 107, 107, + 108, 108, 108, 108, 108, 108, 108, 109, 109, 109, + 109, 109, 109, 110, 110, 110, 110, 110, 111, 111, + 111, 111, 111, 112, 112, 113, 113, 114, 114, 115, + 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, + 120, 121, 121, 122, 122, 123, 123, 90, 90, 124, + 124, 125, 125, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 89, 89, 126, 126, 127, 127, 128, + 128, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 130, 146, 146, 145, + 145, 131, 131, 147, 147, 148, 148, 150, 150, 149, + 151, 154, 152, 152, 155, 153, 153, 132, 133, 133, + 134, 134, 135, 135, 135, 135, 135, 135, 135, 136, + 136, 136, 136, 137, 137, 137, 137, 138, 138, 139, + 141, 156, 156, 159, 159, 157, 157, 160, 158, 140, + 142, 142, 143, 143, 143, 161, 162, 144, 163, 97, + 167, 167, 164, 164, 165, 165, 168, 84, 169, 169, + 170, 170, 166, 166, 88, 88, 171}; + +const int QScriptGrammar:: rhs[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, + 3, 5, 3, 3, 2, 4, 1, 2, 0, 1, + 3, 5, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4, 3, 3, 1, 2, 2, 2, 4, 3, + 2, 3, 1, 3, 1, 1, 1, 2, 2, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, + 3, 3, 3, 1, 3, 3, 1, 3, 3, 3, + 1, 3, 3, 3, 3, 3, 3, 1, 3, 3, + 3, 3, 3, 1, 3, 3, 3, 3, 1, 3, + 3, 3, 3, 1, 3, 1, 3, 1, 3, 1, + 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, + 3, 1, 3, 1, 5, 1, 5, 1, 3, 1, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 0, 1, 1, 3, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 1, 2, 0, + 1, 3, 3, 1, 1, 1, 3, 1, 3, 2, + 2, 2, 0, 1, 2, 0, 1, 1, 2, 2, + 7, 5, 7, 7, 5, 9, 10, 7, 8, 2, + 2, 3, 3, 2, 2, 3, 3, 3, 3, 5, + 5, 3, 5, 1, 2, 0, 1, 4, 3, 3, + 3, 3, 3, 3, 4, 5, 2, 1, 8, 8, + 1, 3, 0, 1, 0, 1, 1, 1, 1, 2, + 1, 1, 0, 1, 0, 1, 2}; + +const int QScriptGrammar::action_default [] = { + 0, 97, 164, 128, 136, 132, 172, 179, 76, 148, + 178, 186, 174, 124, 0, 175, 262, 61, 176, 177, + 182, 77, 140, 144, 65, 94, 75, 80, 60, 0, + 114, 180, 101, 259, 258, 261, 183, 0, 194, 0, + 248, 0, 8, 9, 0, 5, 0, 263, 2, 0, + 265, 19, 0, 0, 0, 0, 0, 3, 6, 0, + 0, 166, 208, 7, 0, 1, 0, 0, 4, 0, + 0, 195, 0, 0, 0, 184, 185, 90, 0, 173, + 181, 0, 0, 77, 96, 263, 2, 265, 79, 78, + 0, 0, 0, 92, 93, 91, 0, 264, 253, 254, + 0, 251, 0, 252, 0, 255, 256, 0, 257, 250, + 260, 0, 266, 0, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 23, + 41, 42, 43, 44, 45, 25, 46, 47, 24, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 0, + 21, 0, 0, 0, 22, 13, 95, 0, 125, 0, + 0, 0, 0, 115, 0, 0, 0, 0, 0, 0, + 105, 0, 0, 0, 99, 100, 98, 103, 107, 106, + 104, 102, 117, 116, 118, 0, 133, 0, 129, 68, + 0, 0, 0, 70, 59, 58, 0, 0, 69, 165, + 0, 73, 71, 0, 72, 74, 209, 210, 0, 161, + 154, 152, 159, 160, 158, 157, 163, 156, 155, 153, + 162, 149, 0, 137, 0, 0, 141, 0, 0, 145, + 67, 0, 0, 63, 0, 62, 267, 224, 0, 225, + 226, 227, 220, 0, 221, 222, 223, 81, 0, 0, + 0, 0, 0, 213, 214, 170, 168, 130, 138, 134, + 150, 126, 171, 0, 77, 142, 146, 119, 108, 0, + 0, 127, 0, 0, 0, 0, 120, 0, 0, 0, + 0, 0, 112, 110, 113, 111, 109, 122, 121, 123, + 0, 135, 0, 131, 0, 169, 77, 0, 151, 166, + 167, 0, 166, 0, 0, 216, 0, 0, 0, 218, + 0, 139, 0, 0, 143, 0, 0, 147, 206, 0, + 198, 207, 201, 0, 205, 0, 166, 199, 0, 166, + 0, 0, 217, 0, 0, 0, 219, 264, 253, 0, + 0, 255, 0, 249, 0, 240, 0, 0, 0, 212, + 0, 211, 188, 191, 0, 27, 30, 31, 248, 34, + 35, 5, 39, 40, 2, 41, 44, 3, 6, 166, + 7, 48, 1, 50, 4, 52, 53, 54, 55, 56, + 57, 189, 187, 65, 66, 64, 0, 228, 229, 0, + 0, 0, 231, 236, 234, 237, 0, 0, 235, 236, + 0, 232, 0, 233, 190, 239, 0, 190, 238, 0, + 241, 242, 0, 190, 243, 244, 0, 0, 245, 0, + 0, 0, 246, 247, 83, 82, 0, 0, 0, 215, + 0, 0, 0, 230, 0, 20, 0, 17, 19, 11, + 0, 16, 12, 18, 15, 10, 0, 14, 87, 85, + 89, 86, 84, 88, 203, 196, 0, 204, 200, 0, + 202, 192, 0, 193, 197}; + +const int QScriptGrammar::goto_default [] = { + 29, 28, 436, 434, 113, 14, 2, 435, 112, 111, + 114, 193, 24, 17, 189, 26, 8, 200, 21, 27, + 77, 25, 1, 32, 30, 267, 13, 261, 3, 257, + 5, 259, 4, 258, 22, 265, 23, 266, 9, 260, + 256, 297, 386, 262, 263, 35, 6, 79, 12, 15, + 18, 19, 10, 7, 31, 80, 20, 36, 75, 76, + 11, 354, 353, 78, 456, 455, 319, 320, 458, 322, + 457, 321, 392, 396, 399, 395, 394, 414, 415, 16, + 100, 107, 96, 99, 106, 108, 33, 0}; + +const int QScriptGrammar::action_index [] = { + 1210, 59, -84, 71, 41, -1, -84, -84, 148, -84, + -84, -84, -84, 201, 130, -84, -84, -84, -84, -84, + -84, 343, 67, 62, 122, 109, -84, -84, -84, 85, + 273, -84, 184, -84, 1210, -84, -84, 119, -84, 112, + -84, 521, -84, -84, 1130, -84, 45, 54, 58, 38, + 1290, 50, 521, 521, 521, 376, 521, -84, -84, 521, + 521, 521, -84, -84, 25, -84, 521, 521, -84, 43, + 521, -84, 521, 18, 15, -84, -84, -84, 24, -84, + -84, 521, 521, 64, 153, 27, -84, 1050, -84, -84, + 521, 521, 521, -84, -84, -84, 28, -84, 37, 55, + 19, -84, 33, -84, 34, 1210, -84, 16, 1210, -84, + -84, 39, 52, -3, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, 521, + -84, 1050, 125, 521, -84, -84, 155, 521, 189, 521, + 521, 521, 521, 248, 521, 521, 521, 521, 521, 521, + 243, 521, 521, 521, 75, 82, 94, 177, 184, 184, + 184, 184, 263, 283, 298, 521, 44, 521, 77, -84, + 970, 521, 817, -84, -84, -84, 95, 521, -84, -84, + 93, -84, -84, 521, -84, -84, -84, -84, 521, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + -84, -84, 521, 41, 521, 521, 68, 66, 521, -84, + -84, 970, 521, -84, 103, -84, -84, -84, 63, -84, + -84, -84, -84, 69, -84, -84, -84, -84, -27, 12, + 521, 92, 100, -84, -84, 890, -84, 31, -13, -45, + -84, 210, 32, -28, 387, 20, 73, 304, 117, -5, + 521, 212, 521, 521, 521, 521, 213, 521, 521, 521, + 521, 521, 151, 150, 176, 158, 168, 304, 304, 228, + 521, -72, 521, 4, 521, -84, 306, 521, -84, 521, + 8, -50, 521, -48, 1130, -84, 521, 80, 1130, -84, + 521, -33, 521, 521, 5, 48, 521, -84, 17, 88, + 11, -84, -84, 521, -84, -29, 521, -84, -41, 521, + -39, 1130, -84, 521, 87, 1130, -84, -8, -2, -35, + 10, 1210, -16, -84, 1130, -84, 521, 86, 1130, -14, + 1130, -84, -84, 1130, -36, 107, -21, 165, 3, 521, + 1130, 6, 14, 61, 7, -19, 448, -4, -6, 671, + 29, 13, 23, 521, 30, -10, 521, 9, 521, -30, + -18, -84, -84, 164, -84, -84, 46, -84, -84, 521, + 111, -24, -84, 36, -84, 40, 99, 521, -84, 21, + 22, -84, -11, -84, 1130, -84, 106, 1130, -84, 178, + -84, -84, 98, 1130, 57, -84, 56, 60, -84, 51, + 26, 35, -84, -84, -84, -84, 521, 97, 1130, -84, + 521, 90, 1130, -84, 79, 76, 744, -84, 49, -84, + 594, -84, -84, -84, -84, -84, 83, -84, -84, -84, + -84, -84, -84, -84, 42, -84, 162, -84, -84, 521, + -84, -84, 53, -84, -84, + + -61, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -4, -88, -88, 22, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -51, -88, -88, -88, -88, -88, + -88, 105, -88, -88, -12, -88, -88, -88, -88, -88, + -7, -88, 35, 132, 62, 154, 79, -88, -88, 100, + 75, 36, -88, -88, -88, -88, 37, 70, -88, -1, + 86, -88, 92, -88, -88, -88, -88, -88, -88, -88, + -88, 90, 95, -88, -88, -88, -88, -88, -88, -88, + 87, 82, 74, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -47, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, 28, + -88, 20, -88, 19, -88, -88, -88, 39, -88, 42, + 43, 106, 61, -88, 63, 55, 52, 53, 91, 125, + -88, 120, 123, 118, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, 116, -88, 59, -88, -88, + 16, 18, 15, -88, -88, -88, -88, 21, -88, -88, + -88, -88, -88, 24, -88, -88, -88, -88, 38, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, 97, -88, 115, 25, -88, -88, 26, -88, + -88, 111, 14, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + 23, -88, -88, -88, -88, 108, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + 160, -88, 171, 163, 145, 179, -88, 135, 45, 41, + 66, 80, -88, -88, -88, -88, -88, -88, -88, -88, + 172, -88, 156, -88, 142, -88, -88, 144, -88, 122, + -88, -88, 114, -88, -23, -88, 48, -88, 29, -88, + 224, -88, 157, 175, -88, -88, 182, -88, -88, -88, + -88, -88, -88, 183, -88, -21, 134, -88, -88, 49, + -88, 3, -88, 44, -88, 2, -88, -88, -37, -88, + -88, -31, -88, -88, 10, -88, 47, -88, 17, -88, + 27, -88, -88, 13, -88, -88, -88, -88, -88, 117, + 6, -88, -88, -88, -88, -88, 154, -88, -88, 1, + -88, -88, -88, 7, -88, -35, 137, -88, 141, -88, + -88, -88, -88, -6, -88, -88, -88, -88, -88, 78, + -88, -88, -88, -88, -88, -69, -88, 11, -88, -59, + -88, -88, -88, -88, 83, -88, -88, 56, -88, -88, + -88, -88, -88, -40, -58, -88, -88, -29, -88, -88, + -88, -45, -88, -88, -88, -88, -3, -88, -42, -88, + -5, -88, -32, -88, -88, -88, 9, -88, 8, -88, + -2, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, 12, + -88, -88, -56, -88, -88}; + +const int QScriptGrammar::action_info [] = { + 318, -25, 350, -45, 292, 270, 426, 310, -194, 393, + -32, 302, 304, -37, 344, 290, 197, 346, 430, 382, + 329, 331, 310, 413, 318, 340, 397, 101, 338, 404, + -49, 292, 270, 299, 323, 290, -24, -51, -195, 343, + 294, 397, 333, 341, 403, 397, 149, 249, 250, 389, + 255, 430, 155, 454, 426, 316, 97, 437, 437, 459, + 151, 389, 103, 102, 98, 344, 101, 105, 413, 222, + 222, 109, 157, 228, 346, 187, 413, 417, 157, 104, + 420, 255, 454, 337, 443, 236, 421, 438, 197, 185, + 97, 197, 419, 413, 197, 197, 325, -263, 197, 81, + 197, 203, 0, 197, 416, 197, 88, 388, 387, 400, + 82, 197, 224, 407, 197, 81, 225, 89, 417, 197, + 187, 90, 81, 312, 241, 240, 82, 313, 0, 0, + 246, 245, 153, 82, 81, 439, 238, 231, 197, 0, + 308, 243, 171, 447, 172, 82, 348, 335, 238, 326, + 432, 198, 252, 204, 401, 173, 232, 428, 192, 235, + 0, 254, 253, 190, 0, 90, 91, 90, 239, 237, + 462, 391, 92, 244, 242, 171, 171, 172, 172, 231, + 239, 237, 191, 171, 192, 172, 197, 0, 173, 173, + 0, 207, 206, 171, 243, 172, 173, 0, 232, 0, + 192, 171, 171, 172, 172, 0, 173, 159, 160, 171, + 91, 172, 91, 0, 173, 173, 92, 0, 92, 159, + 160, 0, 173, 463, 461, 0, 244, 242, 272, 273, + 272, 273, 0, 0, 161, 162, 277, 278, 0, 411, + 410, 0, 0, 0, 0, 279, 161, 162, 280, 0, + 281, 277, 278, 0, 0, 274, 275, 274, 275, 0, + 279, 0, 0, 280, 0, 281, 0, 0, 171, 0, + 172, 164, 165, 0, 0, 0, 0, 0, 0, 166, + 167, 173, 0, 168, 0, 169, 164, 165, 0, 0, + 0, 0, 0, 0, 166, 167, 164, 165, 168, 0, + 169, 0, 0, 0, 166, 167, 164, 165, 168, 209, + 169, 0, 0, 0, 166, 167, 0, 0, 168, 210, + 169, 164, 165, 211, 0, 0, 0, 277, 278, 166, + 167, 0, 212, 168, 213, 169, 279, 0, 0, 280, + 0, 281, 0, 0, 0, 214, 209, 215, 88, 0, + 0, 0, 0, 0, 0, 216, 210, 0, 217, 89, + 211, 0, 0, 0, 218, 0, 0, 0, 0, 212, + 219, 213, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 214, 220, 215, 88, 0, 0, 42, 43, + 209, 0, 216, 0, 0, 217, 89, 0, 85, 0, + 210, 218, 0, 0, 211, 86, 0, 219, 0, 87, + 51, 0, 52, 212, 0, 213, 0, 0, 306, 55, + 220, 0, 0, 58, 0, 0, 214, 0, 215, 88, + 0, 0, 0, 0, 0, 0, 216, 0, 0, 217, + 89, 63, 0, 65, 0, 218, 0, 0, 0, 0, + 0, 219, 0, 0, 57, 68, 45, 0, 0, 0, + 42, 43, 0, 0, 220, 0, 0, 0, 0, 0, + 85, 0, 0, 0, 0, 0, 0, 86, 0, 0, + 0, 87, 51, 0, 52, 0, 0, 0, 0, 0, + 0, 55, 0, 0, 0, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 63, 0, 65, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 57, 68, 45, 0, + 0, 0, 41, 42, 43, 0, 0, 0, 0, 0, + 0, 0, 0, 85, 0, 0, 0, 0, 0, 0, + 86, 0, 0, 0, 87, 51, 0, 52, 0, 0, + 0, 53, 0, 54, 55, 56, 0, 0, 58, 0, + 0, 0, 59, 0, 60, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 63, 0, 65, 0, + 67, 0, 70, 0, 72, 0, 0, 0, 0, 57, + 68, 45, 0, 0, 0, 41, 42, 43, 0, 0, + 0, 0, 0, 0, 0, 0, 85, 0, 0, 0, + 0, 0, 0, 86, 0, 0, 0, 87, 51, 0, + 52, 0, 0, 0, 53, 0, 54, 55, 56, 0, + 0, 58, 0, 0, 0, 59, 0, 60, 0, 0, + 442, 0, 0, 0, 0, 0, 0, 0, 0, 63, + 0, 65, 0, 67, 0, 70, 0, 72, 0, 0, + 0, 0, 57, 68, 45, 0, 0, 0, -47, 0, + 0, 0, 41, 42, 43, 0, 0, 0, 0, 0, + 0, 0, 0, 85, 0, 0, 0, 0, 0, 0, + 86, 0, 0, 0, 87, 51, 0, 52, 0, 0, + 0, 53, 0, 54, 55, 56, 0, 0, 58, 0, + 0, 0, 59, 0, 60, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 63, 0, 65, 0, + 67, 0, 70, 0, 72, 0, 0, 0, 0, 57, + 68, 45, 0, 0, 0, 41, 42, 43, 0, 0, + 0, 0, 0, 0, 0, 0, 85, 0, 0, 0, + 0, 0, 0, 86, 0, 0, 0, 87, 51, 0, + 52, 0, 0, 0, 53, 0, 54, 55, 56, 0, + 0, 58, 0, 0, 0, 59, 0, 60, 0, 0, + 445, 0, 0, 0, 0, 0, 0, 0, 0, 63, + 0, 65, 0, 67, 0, 70, 0, 72, 0, 0, + 0, 0, 57, 68, 45, 0, 0, 0, 41, 42, + 43, 0, 0, 0, 0, 0, 0, 0, 0, 85, + 0, 0, 0, 0, 0, 0, 86, 0, 0, 0, + 87, 51, 0, 52, 0, 0, 0, 53, 0, 54, + 55, 56, 0, 0, 58, 0, 0, 0, 59, 0, + 60, 0, 0, 0, 0, 0, 0, 202, 0, 0, + 0, 0, 63, 0, 65, 0, 67, 0, 70, 0, + 72, 0, 0, 0, 0, 57, 68, 45, 0, 0, + 0, 41, 42, 43, 0, 0, 0, 0, 0, 0, + 0, 0, 85, 0, 0, 0, 0, 0, 0, 86, + 0, 0, 0, 87, 51, 0, 52, 0, 0, 0, + 53, 0, 54, 55, 56, 0, 0, 58, 0, 0, + 0, 59, 0, 60, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 63, 0, 65, 0, 67, + 0, 70, 269, 72, 0, 0, 0, 0, 57, 68, + 45, 0, 0, 0, 115, 116, 117, 0, 0, 119, + 121, 122, 0, 0, 123, 0, 124, 0, 0, 0, + 126, 127, 128, 0, 0, 0, 0, 0, 0, 195, + 130, 131, 132, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 133, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 137, + 0, 0, 0, 0, 0, 0, 139, 140, 141, 0, + 143, 144, 145, 146, 147, 148, 0, 0, 134, 142, + 125, 118, 120, 136, 115, 116, 117, 0, 0, 119, + 121, 122, 0, 0, 123, 0, 124, 0, 0, 0, + 126, 127, 128, 0, 0, 0, 0, 0, 0, 129, + 130, 131, 132, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 133, 0, 0, 0, 135, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 137, + 0, 0, 0, 0, 0, 138, 139, 140, 141, 0, + 143, 144, 145, 146, 147, 148, 0, 0, 134, 142, + 125, 118, 120, 136, 37, 0, 0, 0, 0, 39, + 0, 41, 42, 43, 44, 0, 0, 0, 0, 0, + 0, 46, 85, 0, 0, 0, 0, 0, 0, 48, + 49, 0, 0, 50, 51, 0, 52, 0, 0, 0, + 53, 0, 54, 55, 56, 0, 0, 58, 0, 0, + 0, 59, 0, 60, 0, 0, 0, 0, 0, 61, + 0, 62, 0, 0, 0, 63, 64, 65, 66, 67, + 69, 70, 71, 72, 73, 74, 0, 0, 57, 68, + 45, 38, 40, 0, 37, 0, 0, 0, 0, 39, + 0, 41, 42, 43, 44, 0, 0, 0, 0, 0, + 0, 46, 47, 0, 0, 0, 0, 0, 0, 48, + 49, 0, 0, 50, 51, 0, 52, 0, 0, 0, + 53, 0, 54, 55, 56, 0, 0, 58, 0, 0, + 0, 59, 0, 60, 0, 0, 0, 0, 0, 61, + 0, 62, 0, 0, 0, 63, 64, 65, 66, 67, + 69, 70, 71, 72, 73, 74, 0, 0, 57, 68, + 45, 38, 40, 0, 355, 116, 117, 0, 0, 357, + 121, 359, 42, 43, 360, 0, 124, 0, 0, 0, + 126, 362, 363, 0, 0, 0, 0, 0, 0, 364, + 365, 131, 132, 50, 51, 0, 52, 0, 0, 0, + 53, 0, 54, 366, 56, 0, 0, 368, 0, 0, + 0, 59, 0, 60, 0, -190, 0, 0, 0, 369, + 0, 62, 0, 0, 0, 370, 371, 372, 373, 67, + 375, 376, 377, 378, 379, 380, 0, 0, 367, 374, + 361, 356, 358, 136, + + 431, 422, 427, 429, 441, 352, 300, 398, 385, 464, + 440, 412, 409, 433, 402, 444, 406, 423, 460, 234, + 418, 201, 305, 196, 34, 154, 194, 199, 251, 152, + 205, 227, 229, 248, 150, 110, 230, 208, 352, 110, + 446, 300, 409, 339, 221, 412, 327, 336, 332, 334, + 342, 248, 347, 307, 300, 345, 0, 83, 381, 83, + 83, 83, 349, 83, 284, 158, 163, 182, 283, 0, + 83, 83, 351, 83, 309, 178, 179, 83, 177, 83, + 83, 83, 449, 390, 83, 184, 170, 188, 83, 285, + 453, 330, 83, 83, 95, 452, 0, 83, 83, 450, + 83, 352, 94, 286, 83, 83, 424, 93, 83, 83, + 83, 84, 425, 83, 180, 83, 156, 408, 83, 300, + 451, 194, 233, 83, 83, 247, 264, 300, 352, 223, + 183, 268, 0, 83, 83, 83, 83, 247, 83, 300, + 176, 83, 174, 83, 405, 175, 186, 0, 181, 226, + 83, 0, 448, 83, 0, 83, 303, 424, 282, 83, + 296, 425, 296, 83, 301, 268, 383, 268, 268, 384, + 288, 0, 0, 0, 83, 83, 328, 0, 83, 268, + 268, 83, 295, 268, 298, 293, 268, 271, 287, 83, + 83, 0, 314, 296, 268, 268, 276, 83, 268, 0, + 296, 296, 268, 291, 289, 268, 268, 0, 0, 0, + 0, 0, 0, 0, 0, 315, 0, 0, 0, 0, + 0, 0, 317, 324, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 83, 0, 0, 0, 0, 268, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 311, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}; + +const int QScriptGrammar::action_check [] = { + 29, 7, 16, 7, 76, 1, 36, 2, 29, 33, + 7, 61, 60, 7, 7, 48, 8, 36, 36, 55, + 61, 60, 2, 33, 29, 60, 5, 29, 36, 7, + 7, 76, 1, 61, 17, 48, 7, 7, 29, 55, + 8, 5, 31, 33, 55, 5, 7, 74, 36, 36, + 36, 36, 55, 29, 36, 7, 29, 8, 8, 17, + 8, 36, 29, 8, 36, 7, 29, 33, 33, 2, + 2, 55, 1, 7, 36, 76, 33, 20, 1, 60, + 29, 36, 29, 29, 8, 0, 60, 8, 8, 48, + 29, 8, 36, 33, 8, 8, 8, 36, 8, 40, + 8, 8, -1, 8, 6, 8, 42, 61, 62, 10, + 51, 8, 50, 7, 8, 40, 54, 53, 20, 8, + 76, 12, 40, 50, 61, 62, 51, 54, -1, -1, + 61, 62, 7, 51, 40, 56, 29, 15, 8, -1, + 60, 29, 25, 60, 27, 51, 60, 60, 29, 61, + 60, 56, 60, 60, 55, 38, 34, 60, 36, 56, + -1, 61, 62, 15, -1, 12, 57, 12, 61, 62, + 8, 60, 63, 61, 62, 25, 25, 27, 27, 15, + 61, 62, 34, 25, 36, 27, 8, -1, 38, 38, + -1, 61, 62, 25, 29, 27, 38, -1, 34, -1, + 36, 25, 25, 27, 27, -1, 38, 18, 19, 25, + 57, 27, 57, -1, 38, 38, 63, -1, 63, 18, + 19, -1, 38, 61, 62, -1, 61, 62, 18, 19, + 18, 19, -1, -1, 45, 46, 23, 24, -1, 61, + 62, -1, -1, -1, -1, 32, 45, 46, 35, -1, + 37, 23, 24, -1, -1, 45, 46, 45, 46, -1, + 32, -1, -1, 35, -1, 37, -1, -1, 25, -1, + 27, 23, 24, -1, -1, -1, -1, -1, -1, 31, + 32, 38, -1, 35, -1, 37, 23, 24, -1, -1, + -1, -1, -1, -1, 31, 32, 23, 24, 35, -1, + 37, -1, -1, -1, 31, 32, 23, 24, 35, 3, + 37, -1, -1, -1, 31, 32, -1, -1, 35, 13, + 37, 23, 24, 17, -1, -1, -1, 23, 24, 31, + 32, -1, 26, 35, 28, 37, 32, -1, -1, 35, + -1, 37, -1, -1, -1, 39, 3, 41, 42, -1, + -1, -1, -1, -1, -1, 49, 13, -1, 52, 53, + 17, -1, -1, -1, 58, -1, -1, -1, -1, 26, + 64, 28, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 39, 77, 41, 42, -1, -1, 12, 13, + 3, -1, 49, -1, -1, 52, 53, -1, 22, -1, + 13, 58, -1, -1, 17, 29, -1, 64, -1, 33, + 34, -1, 36, 26, -1, 28, -1, -1, 31, 43, + 77, -1, -1, 47, -1, -1, 39, -1, 41, 42, + -1, -1, -1, -1, -1, -1, 49, -1, -1, 52, + 53, 65, -1, 67, -1, 58, -1, -1, -1, -1, + -1, 64, -1, -1, 78, 79, 80, -1, -1, -1, + 12, 13, -1, -1, 77, -1, -1, -1, -1, -1, + 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, + -1, 33, 34, -1, 36, -1, -1, -1, -1, -1, + -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 65, -1, 67, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 78, 79, 80, -1, + -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, + -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, + 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, + -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, + -1, -1, 51, -1, 53, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 65, -1, 67, -1, + 69, -1, 71, -1, 73, -1, -1, -1, -1, 78, + 79, 80, -1, -1, -1, 11, 12, 13, -1, -1, + -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, + -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, + 36, -1, -1, -1, 40, -1, 42, 43, 44, -1, + -1, 47, -1, -1, -1, 51, -1, 53, -1, -1, + 56, -1, -1, -1, -1, -1, -1, -1, -1, 65, + -1, 67, -1, 69, -1, 71, -1, 73, -1, -1, + -1, -1, 78, 79, 80, -1, -1, -1, 7, -1, + -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, + -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, + 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, + -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, + -1, -1, 51, -1, 53, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 65, -1, 67, -1, + 69, -1, 71, -1, 73, -1, -1, -1, -1, 78, + 79, 80, -1, -1, -1, 11, 12, 13, -1, -1, + -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, + -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, + 36, -1, -1, -1, 40, -1, 42, 43, 44, -1, + -1, 47, -1, -1, -1, 51, -1, 53, -1, -1, + 56, -1, -1, -1, -1, -1, -1, -1, -1, 65, + -1, 67, -1, 69, -1, 71, -1, 73, -1, -1, + -1, -1, 78, 79, 80, -1, -1, -1, 11, 12, + 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, + -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, + 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, + 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, + 53, -1, -1, -1, -1, -1, -1, 60, -1, -1, + -1, -1, 65, -1, 67, -1, 69, -1, 71, -1, + 73, -1, -1, -1, -1, 78, 79, 80, -1, -1, + -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, + -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, + -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, + 40, -1, 42, 43, 44, -1, -1, 47, -1, -1, + -1, 51, -1, 53, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 65, -1, 67, -1, 69, + -1, 71, 72, 73, -1, -1, -1, -1, 78, 79, + 80, -1, -1, -1, 4, 5, 6, -1, -1, 9, + 10, 11, -1, -1, 14, -1, 16, -1, -1, -1, + 20, 21, 22, -1, -1, -1, -1, -1, -1, 29, + 30, 31, 32, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 43, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 59, + -1, -1, -1, -1, -1, -1, 66, 67, 68, -1, + 70, 71, 72, 73, 74, 75, -1, -1, 78, 79, + 80, 81, 82, 83, 4, 5, 6, -1, -1, 9, + 10, 11, -1, -1, 14, -1, 16, -1, -1, -1, + 20, 21, 22, -1, -1, -1, -1, -1, -1, 29, + 30, 31, 32, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 59, + -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, + 70, 71, 72, 73, 74, 75, -1, -1, 78, 79, + 80, 81, 82, 83, 4, -1, -1, -1, -1, 9, + -1, 11, 12, 13, 14, -1, -1, -1, -1, -1, + -1, 21, 22, -1, -1, -1, -1, -1, -1, 29, + 30, -1, -1, 33, 34, -1, 36, -1, -1, -1, + 40, -1, 42, 43, 44, -1, -1, 47, -1, -1, + -1, 51, -1, 53, -1, -1, -1, -1, -1, 59, + -1, 61, -1, -1, -1, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, -1, -1, 78, 79, + 80, 81, 82, -1, 4, -1, -1, -1, -1, 9, + -1, 11, 12, 13, 14, -1, -1, -1, -1, -1, + -1, 21, 22, -1, -1, -1, -1, -1, -1, 29, + 30, -1, -1, 33, 34, -1, 36, -1, -1, -1, + 40, -1, 42, 43, 44, -1, -1, 47, -1, -1, + -1, 51, -1, 53, -1, -1, -1, -1, -1, 59, + -1, 61, -1, -1, -1, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, -1, -1, 78, 79, + 80, 81, 82, -1, 4, 5, 6, -1, -1, 9, + 10, 11, 12, 13, 14, -1, 16, -1, -1, -1, + 20, 21, 22, -1, -1, -1, -1, -1, -1, 29, + 30, 31, 32, 33, 34, -1, 36, -1, -1, -1, + 40, -1, 42, 43, 44, -1, -1, 47, -1, -1, + -1, 51, -1, 53, -1, 55, -1, -1, -1, 59, + -1, 61, -1, -1, -1, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, -1, -1, 78, 79, + 80, 81, 82, 83, + + 5, 46, 5, 45, 6, 45, 5, 76, 14, 65, + 2, 46, 5, 45, 73, 6, 5, 46, 6, 5, + 78, 6, 45, 5, 85, 6, 10, 6, 5, 9, + 6, 6, 6, 45, 6, 86, 14, 41, 45, 86, + 5, 5, 5, 80, 6, 46, 67, 45, 45, 5, + 81, 45, 5, 5, 5, 45, -1, 18, 45, 18, + 18, 18, 45, 18, 23, 26, 24, 24, 23, -1, + 18, 18, 45, 18, 45, 23, 23, 18, 23, 18, + 18, 18, 20, 5, 18, 24, 23, 28, 18, 23, + 20, 42, 18, 18, 20, 20, -1, 18, 18, 20, + 18, 45, 20, 23, 18, 18, 20, 20, 18, 18, + 18, 21, 20, 18, 23, 18, 21, 61, 18, 5, + 20, 10, 11, 18, 18, 20, 18, 5, 45, 32, + 24, 23, -1, 18, 18, 18, 18, 20, 18, 5, + 22, 18, 22, 18, 61, 22, 30, -1, 23, 34, + 18, -1, 20, 18, -1, 18, 42, 20, 23, 18, + 18, 20, 18, 18, 42, 23, 12, 23, 23, 15, + 25, -1, -1, -1, 18, 18, 42, -1, 18, 23, + 23, 18, 40, 23, 40, 29, 23, 27, 25, 18, + 18, -1, 35, 18, 23, 23, 25, 18, 23, -1, + 18, 18, 23, 31, 25, 23, 23, -1, -1, -1, + -1, -1, -1, -1, -1, 40, -1, -1, -1, -1, + -1, -1, 40, 40, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 18, -1, -1, -1, -1, 23, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 33, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}; + + +#define Q_SCRIPT_REGEXPLITERAL_RULE1 7 + +#define Q_SCRIPT_REGEXPLITERAL_RULE2 8 + +#include <translator.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qnumeric.h> +#include <QtCore/qstring.h> +#include <QtCore/qtextcodec.h> +#include <QtCore/qvariant.h> + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +QT_BEGIN_NAMESPACE + +static void recordMessage( + Translator *tor, const QString &context, const QString &text, const QString &comment, + const QString &extracomment, bool plural, const QString &fileName, int lineNo) +{ + TranslatorMessage msg( + context, text, comment, QString(), + fileName, lineNo, QStringList(), + TranslatorMessage::Unfinished, plural); + msg.setExtraComment(extracomment.simplified()); + tor->replace(msg); +} + + +namespace QScript +{ + +class Lexer +{ +public: + Lexer(); + ~Lexer(); + + void setCode(const QString &c, int lineno); + int lex(); + + int currentLineNo() const { return yylineno; } + int currentColumnNo() const { return yycolumn; } + + int startLineNo() const { return startlineno; } + int startColumnNo() const { return startcolumn; } + + int endLineNo() const { return currentLineNo(); } + int endColumnNo() const + { int col = currentColumnNo(); return (col > 0) ? col - 1 : col; } + + bool prevTerminator() const { return terminator; } + + enum State { Start, + Identifier, + InIdentifier, + InSingleLineComment, + InMultiLineComment, + InNum, + InNum0, + InHex, + InOctal, + InDecimal, + InExponentIndicator, + InExponent, + Hex, + Octal, + Number, + String, + Eof, + InString, + InEscapeSequence, + InHexEscape, + InUnicodeEscape, + Other, + Bad }; + + enum Error { + NoError, + IllegalCharacter, + UnclosedStringLiteral, + IllegalEscapeSequence, + IllegalUnicodeEscapeSequence, + UnclosedComment, + IllegalExponentIndicator, + IllegalIdentifier + }; + + enum ParenthesesState { + IgnoreParentheses, + CountParentheses, + BalancedParentheses + }; + + enum RegExpBodyPrefix { + NoPrefix, + EqualPrefix + }; + + bool scanRegExp(RegExpBodyPrefix prefix = NoPrefix); + + QString pattern; + int flags; + + State lexerState() const + { return state; } + + QString errorMessage() const + { return errmsg; } + void setErrorMessage(const QString &err) + { errmsg = err; } + void setErrorMessage(const char *err) + { setErrorMessage(QString::fromLatin1(err)); } + + Error error() const + { return err; } + void clearError() + { err = NoError; } + +private: + int yylineno; + bool done; + char *buffer8; + QChar *buffer16; + uint size8, size16; + uint pos8, pos16; + bool terminator; + bool restrKeyword; + // encountered delimiter like "'" and "}" on last run + bool delimited; + int stackToken; + + State state; + void setDone(State s); + uint pos; + void shift(uint p); + int lookupKeyword(const char *); + + bool isWhiteSpace() const; + bool isLineTerminator() const; + bool isHexDigit(ushort c) const; + bool isOctalDigit(ushort c) const; + + int matchPunctuator(ushort c1, ushort c2, + ushort c3, ushort c4); + ushort singleEscape(ushort c) const; + ushort convertOctal(ushort c1, ushort c2, + ushort c3) const; +public: + static unsigned char convertHex(ushort c1); + static unsigned char convertHex(ushort c1, ushort c2); + static QChar convertUnicode(ushort c1, ushort c2, + ushort c3, ushort c4); + static bool isIdentLetter(ushort c); + static bool isDecimalDigit(ushort c); + + inline int ival() const { return qsyylval.toInt(); } + inline double dval() const { return qsyylval.toDouble(); } + inline QString ustr() const { return qsyylval.toString(); } + inline QVariant val() const { return qsyylval; } + + const QChar *characterBuffer() const { return buffer16; } + int characterCount() const { return pos16; } + +private: + void record8(ushort c); + void record16(QChar c); + void recordStartPos(); + + int findReservedWord(const QChar *buffer, int size) const; + + void syncProhibitAutomaticSemicolon(); + + const QChar *code; + uint length; + int yycolumn; + int startlineno; + int startcolumn; + int bol; // begin of line + + QVariant qsyylval; + + // current and following unicode characters + ushort current, next1, next2, next3; + + struct keyword { + const char *name; + int token; + }; + + QString errmsg; + Error err; + + bool wantRx; + bool check_reserved; + + ParenthesesState parenthesesState; + int parenthesesCount; + bool prohibitAutomaticSemicolon; +}; + +} // namespace QScript + +extern double qstrtod(const char *s00, char const **se, bool *ok); + +#define shiftWindowsLineBreak() if(current == '\r' && next1 == '\n') shift(1); + +namespace QScript { + +static 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 qSNaN(); + + 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 = qSNaN(); + } else { + result = 0; + double multiplier = 1; + for (--i ; i >= j; --i, multiplier *= radix) + result += toDigit(buf[i]) * multiplier; + } + result *= sign; + return result; +} + +} // namespace QScript + +QScript::Lexer::Lexer() + : + yylineno(0), + size8(128), size16(128), restrKeyword(false), + stackToken(-1), pos(0), + code(0), length(0), + bol(true), + current(0), next1(0), next2(0), next3(0), + err(NoError), + check_reserved(true), + parenthesesState(IgnoreParentheses), + prohibitAutomaticSemicolon(false) +{ + // allocate space for read buffers + buffer8 = new char[size8]; + buffer16 = new QChar[size16]; + flags = 0; + +} + +QScript::Lexer::~Lexer() +{ + delete [] buffer8; + delete [] buffer16; +} + +void QScript::Lexer::setCode(const QString &c, int lineno) +{ + errmsg = QString(); + yylineno = lineno; + yycolumn = 1; + restrKeyword = false; + delimited = false; + stackToken = -1; + pos = 0; + code = c.unicode(); + length = c.length(); + bol = true; + + // read first characters + current = (length > 0) ? code[0].unicode() : 0; + next1 = (length > 1) ? code[1].unicode() : 0; + next2 = (length > 2) ? code[2].unicode() : 0; + next3 = (length > 3) ? code[3].unicode() : 0; +} + +void QScript::Lexer::shift(uint p) +{ + while (p--) { + ++pos; + ++yycolumn; + current = next1; + next1 = next2; + next2 = next3; + next3 = (pos + 3 < length) ? code[pos+3].unicode() : 0; + } +} + +void QScript::Lexer::setDone(State s) +{ + state = s; + done = true; +} + +int QScript::Lexer::findReservedWord(const QChar *c, int size) const +{ + switch (size) { + case 2: { + if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('o')) + return QScriptGrammar::T_DO; + else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('f')) + return QScriptGrammar::T_IF; + else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n')) + return QScriptGrammar::T_IN; + } break; + + case 3: { + if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('o') && c[2] == QLatin1Char('r')) + return QScriptGrammar::T_FOR; + else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('e') && c[2] == QLatin1Char('w')) + return QScriptGrammar::T_NEW; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') && c[2] == QLatin1Char('y')) + return QScriptGrammar::T_TRY; + else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('a') && c[2] == QLatin1Char('r')) + return QScriptGrammar::T_VAR; + else if (check_reserved) { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') && c[2] == QLatin1Char('t')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + case 4: { + if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e')) + return QScriptGrammar::T_CASE; + else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('l') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e')) + return QScriptGrammar::T_ELSE; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('s')) + return QScriptGrammar::T_THIS; + else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('d')) + return QScriptGrammar::T_VOID; + else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('i') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('h')) + return QScriptGrammar::T_WITH; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('e')) + return QScriptGrammar::T_TRUE; + else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('l')) + return QScriptGrammar::T_NULL; + else if (check_reserved) { + if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('n') + && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('m')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('y') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('l') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('g')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('r')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('g') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('o')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + case 5: { + if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('e') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('k')) + return QScriptGrammar::T_BREAK; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('c') + && c[4] == QLatin1Char('h')) + return QScriptGrammar::T_CATCH; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('r') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('w')) + return QScriptGrammar::T_THROW; + else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('l') + && c[4] == QLatin1Char('e')) + return QScriptGrammar::T_WHILE; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('s') + && c[4] == QLatin1Char('t')) + return QScriptGrammar::T_CONST; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('s') + && c[4] == QLatin1Char('e')) + return QScriptGrammar::T_FALSE; + else if (check_reserved) { + if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('r') + && c[4] == QLatin1Char('t')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('r')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('i') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('l')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('l') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('s') + && c[4] == QLatin1Char('s')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('l') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('t')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + case 6: { + if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('e')) + return QScriptGrammar::T_DELETE; + else if (c[0] == QLatin1Char('r') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('u') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('n')) + return QScriptGrammar::T_RETURN; + else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('w') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('c') && c[5] == QLatin1Char('h')) + return QScriptGrammar::T_SWITCH; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('y') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('o') && c[5] == QLatin1Char('f')) + return QScriptGrammar::T_TYPEOF; + else if (check_reserved) { + if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('x') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('t') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('c')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('b') + && c[4] == QLatin1Char('l') && c[5] == QLatin1Char('e')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('b') && c[3] == QLatin1Char('l') + && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('c')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('i') + && c[4] == QLatin1Char('v') && c[5] == QLatin1Char('e')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('r') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('w') && c[5] == QLatin1Char('s')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + case 7: { + if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('f') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('u') && c[5] == QLatin1Char('l') + && c[6] == QLatin1Char('t')) + return QScriptGrammar::T_DEFAULT; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('i') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('l') && c[5] == QLatin1Char('l') + && c[6] == QLatin1Char('y')) + return QScriptGrammar::T_FINALLY; + else if (check_reserved) { + if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('l') + && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('a') + && c[6] == QLatin1Char('n')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('x') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('n') && c[5] == QLatin1Char('d') + && c[6] == QLatin1Char('s')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('c') && c[3] == QLatin1Char('k') + && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('g') + && c[6] == QLatin1Char('e')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('v') + && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('t') + && c[6] == QLatin1Char('e')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + case 8: { + if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('n') + && c[6] == QLatin1Char('u') && c[7] == QLatin1Char('e')) + return QScriptGrammar::T_CONTINUE; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('c') + && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('i') + && c[6] == QLatin1Char('o') && c[7] == QLatin1Char('n')) + return QScriptGrammar::T_FUNCTION; + else if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('b') && c[3] == QLatin1Char('u') + && c[4] == QLatin1Char('g') && c[5] == QLatin1Char('g') + && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('r')) + return QScriptGrammar::T_DEBUGGER; + else if (check_reserved) { + if (c[0] == QLatin1Char('a') && c[1] == QLatin1Char('b') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('a') + && c[6] == QLatin1Char('c') && c[7] == QLatin1Char('t')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('i') + && c[6] == QLatin1Char('l') && c[7] == QLatin1Char('e')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + case 9: { + if (check_reserved) { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('f') + && c[6] == QLatin1Char('a') && c[7] == QLatin1Char('c') + && c[8] == QLatin1Char('e')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('n') + && c[4] == QLatin1Char('s') && c[5] == QLatin1Char('i') + && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('n') + && c[8] == QLatin1Char('t')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('c') + && c[6] == QLatin1Char('t') && c[7] == QLatin1Char('e') + && c[8] == QLatin1Char('d')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + case 10: { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('n') + && c[6] == QLatin1Char('c') && c[7] == QLatin1Char('e') + && c[8] == QLatin1Char('o') && c[9] == QLatin1Char('f')) + return QScriptGrammar::T_INSTANCEOF; + else if (check_reserved) { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('l') + && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('m') + && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('n') + && c[8] == QLatin1Char('t') && c[9] == QLatin1Char('s')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + case 12: { + if (check_reserved) { + if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('y') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('c') + && c[4] == QLatin1Char('h') && c[5] == QLatin1Char('r') + && c[6] == QLatin1Char('o') && c[7] == QLatin1Char('n') + && c[8] == QLatin1Char('i') && c[9] == QLatin1Char('z') + && c[10] == QLatin1Char('e') && c[11] == QLatin1Char('d')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + } // switch + + return -1; +} + +int QScript::Lexer::lex() +{ + int token = 0; + state = Start; + ushort stringType = 0; // either single or double quotes + pos8 = pos16 = 0; + done = false; + terminator = false; + + // did we push a token on the stack previously ? + // (after an automatic semicolon insertion) + if (stackToken >= 0) { + setDone(Other); + token = stackToken; + stackToken = -1; + } + + while (!done) { + switch (state) { + case Start: + if (isWhiteSpace()) { + // do nothing + } else if (current == '/' && next1 == '/') { + recordStartPos(); + shift(1); + state = InSingleLineComment; + } else if (current == '/' && next1 == '*') { + recordStartPos(); + shift(1); + state = InMultiLineComment; + } else if (current == 0) { + syncProhibitAutomaticSemicolon(); + if (!terminator && !delimited && !prohibitAutomaticSemicolon) { + // automatic semicolon insertion if program incomplete + token = QScriptGrammar::T_SEMICOLON; + stackToken = 0; + setDone(Other); + } else { + setDone(Eof); + } + } else if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + yycolumn = 0; + bol = true; + terminator = true; + syncProhibitAutomaticSemicolon(); + if (restrKeyword) { + token = QScriptGrammar::T_SEMICOLON; + setDone(Other); + } + } else if (current == '"' || current == '\'') { + recordStartPos(); + state = InString; + stringType = current; + } else if (isIdentLetter(current)) { + recordStartPos(); + record16(current); + state = InIdentifier; + } else if (current == '0') { + recordStartPos(); + record8(current); + state = InNum0; + } else if (isDecimalDigit(current)) { + recordStartPos(); + record8(current); + state = InNum; + } else if (current == '.' && isDecimalDigit(next1)) { + recordStartPos(); + record8(current); + state = InDecimal; + } else { + recordStartPos(); + token = matchPunctuator(current, next1, next2, next3); + if (token != -1) { + if (terminator && !delimited && !prohibitAutomaticSemicolon + && (token == QScriptGrammar::T_PLUS_PLUS + || token == QScriptGrammar::T_MINUS_MINUS)) { + // automatic semicolon insertion + stackToken = token; + token = QScriptGrammar::T_SEMICOLON; + } + setDone(Other); + } + else { + setDone(Bad); + err = IllegalCharacter; + errmsg = QLatin1String("Illegal character"); + } + } + break; + case InString: + if (current == stringType) { + shift(1); + setDone(String); + } else if (current == 0 || isLineTerminator()) { + setDone(Bad); + err = UnclosedStringLiteral; + errmsg = QLatin1String("Unclosed string at end of line"); + } else if (current == '\\') { + state = InEscapeSequence; + } else { + record16(current); + } + break; + // Escape Sequences inside of strings + case InEscapeSequence: + if (isOctalDigit(current)) { + if (current >= '0' && current <= '3' && + isOctalDigit(next1) && isOctalDigit(next2)) { + record16(convertOctal(current, next1, next2)); + shift(2); + state = InString; + } else if (isOctalDigit(current) && + isOctalDigit(next1)) { + record16(convertOctal('0', current, next1)); + shift(1); + state = InString; + } else if (isOctalDigit(current)) { + record16(convertOctal('0', '0', current)); + state = InString; + } else { + setDone(Bad); + err = IllegalEscapeSequence; + errmsg = QLatin1String("Illegal escape squence"); + } + } else if (current == 'x') + state = InHexEscape; + else if (current == 'u') + state = InUnicodeEscape; + else { + record16(singleEscape(current)); + state = InString; + } + break; + case InHexEscape: + if (isHexDigit(current) && isHexDigit(next1)) { + state = InString; + record16(QLatin1Char(convertHex(current, next1))); + shift(1); + } else if (current == stringType) { + record16(QLatin1Char('x')); + shift(1); + setDone(String); + } else { + record16(QLatin1Char('x')); + record16(current); + state = InString; + } + break; + case InUnicodeEscape: + if (isHexDigit(current) && isHexDigit(next1) && + isHexDigit(next2) && isHexDigit(next3)) { + record16(convertUnicode(current, next1, next2, next3)); + shift(3); + state = InString; + } else if (current == stringType) { + record16(QLatin1Char('u')); + shift(1); + setDone(String); + } else { + setDone(Bad); + err = IllegalUnicodeEscapeSequence; + errmsg = QLatin1String("Illegal unicode escape sequence"); + } + break; + case InSingleLineComment: + if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + yycolumn = 0; + terminator = true; + bol = true; + if (restrKeyword) { + token = QScriptGrammar::T_SEMICOLON; + setDone(Other); + } else + state = Start; + } else if (current == 0) { + setDone(Eof); + } + break; + case InMultiLineComment: + if (current == 0) { + setDone(Bad); + err = UnclosedComment; + errmsg = QLatin1String("Unclosed comment at end of file"); + } else if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + } else if (current == '*' && next1 == '/') { + state = Start; + shift(1); + } + break; + case InIdentifier: + if (isIdentLetter(current) || isDecimalDigit(current)) { + record16(current); + break; + } + setDone(Identifier); + break; + case InNum0: + if (current == 'x' || current == 'X') { + record8(current); + state = InHex; + } else if (current == '.') { + record8(current); + state = InDecimal; + } else if (current == 'e' || current == 'E') { + record8(current); + state = InExponentIndicator; + } else if (isOctalDigit(current)) { + record8(current); + state = InOctal; + } else if (isDecimalDigit(current)) { + record8(current); + state = InDecimal; + } else { + setDone(Number); + } + break; + case InHex: + if (isHexDigit(current)) + record8(current); + else + setDone(Hex); + break; + case InOctal: + if (isOctalDigit(current)) { + record8(current); + } else if (isDecimalDigit(current)) { + record8(current); + state = InDecimal; + } else { + setDone(Octal); + } + break; + case InNum: + if (isDecimalDigit(current)) { + record8(current); + } else if (current == '.') { + record8(current); + state = InDecimal; + } else if (current == 'e' || current == 'E') { + record8(current); + state = InExponentIndicator; + } else { + setDone(Number); + } + break; + case InDecimal: + if (isDecimalDigit(current)) { + record8(current); + } else if (current == 'e' || current == 'E') { + record8(current); + state = InExponentIndicator; + } else { + setDone(Number); + } + break; + case InExponentIndicator: + if (current == '+' || current == '-') { + record8(current); + } else if (isDecimalDigit(current)) { + record8(current); + state = InExponent; + } else { + setDone(Bad); + err = IllegalExponentIndicator; + errmsg = QLatin1String("Illegal syntax for exponential number"); + } + break; + case InExponent: + if (isDecimalDigit(current)) { + record8(current); + } else { + setDone(Number); + } + break; + default: + Q_ASSERT_X(0, "Lexer::lex", "Unhandled state in switch statement"); + } + + // move on to the next character + if (!done) + shift(1); + if (state != Start && state != InSingleLineComment) + bol = false; + } + + // no identifiers allowed directly after numeric literal, e.g. "3in" is bad + if ((state == Number || state == Octal || state == Hex) + && isIdentLetter(current)) { + state = Bad; + err = IllegalIdentifier; + errmsg = QLatin1String("Identifier cannot start with numeric literal"); + } + + // terminate string + buffer8[pos8] = '\0'; + + double dval = 0; + if (state == Number) { + dval = qstrtod(buffer8, 0, 0); + } else if (state == Hex) { // scan hex numbers + dval = QScript::integerFromString(buffer8, pos8, 16); + state = Number; + } else if (state == Octal) { // scan octal number + dval = QScript::integerFromString(buffer8, pos8, 8); + state = Number; + } + + restrKeyword = false; + delimited = false; + + switch (parenthesesState) { + case IgnoreParentheses: + break; + case CountParentheses: + if (token == QScriptGrammar::T_RPAREN) { + --parenthesesCount; + if (parenthesesCount == 0) + parenthesesState = BalancedParentheses; + } else if (token == QScriptGrammar::T_LPAREN) { + ++parenthesesCount; + } + break; + case BalancedParentheses: + parenthesesState = IgnoreParentheses; + break; + } + + switch (state) { + case Eof: + return 0; + case Other: + if(token == QScriptGrammar::T_RBRACE || token == QScriptGrammar::T_SEMICOLON) + delimited = true; + return token; + case Identifier: + if ((token = findReservedWord(buffer16, pos16)) < 0) { + /* TODO: close leak on parse error. same holds true for String */ + qsyylval = QString(buffer16, pos16); + return QScriptGrammar::T_IDENTIFIER; + } + if (token == QScriptGrammar::T_CONTINUE || token == QScriptGrammar::T_BREAK + || token == QScriptGrammar::T_RETURN || token == QScriptGrammar::T_THROW) { + restrKeyword = true; + } else if (token == QScriptGrammar::T_IF || token == QScriptGrammar::T_FOR + || token == QScriptGrammar::T_WHILE || token == QScriptGrammar::T_WITH) { + parenthesesState = CountParentheses; + parenthesesCount = 0; + } else if (token == QScriptGrammar::T_DO) { + parenthesesState = BalancedParentheses; + } + return token; + case String: + qsyylval = QString(buffer16, pos16); + return QScriptGrammar::T_STRING_LITERAL; + case Number: + qsyylval = dval; + return QScriptGrammar::T_NUMERIC_LITERAL; + case Bad: + return -1; + default: + Q_ASSERT(!"unhandled numeration value in switch"); + return -1; + } +} + +bool QScript::Lexer::isWhiteSpace() const +{ + return (current == ' ' || current == '\t' || + current == 0x0b || current == 0x0c); +} + +bool QScript::Lexer::isLineTerminator() const +{ + return (current == '\n' || current == '\r'); +} + +bool QScript::Lexer::isIdentLetter(ushort c) +{ + /* TODO: allow other legitimate unicode chars */ + return ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || c == '$' + || c == '_'); +} + +bool QScript::Lexer::isDecimalDigit(ushort c) +{ + return (c >= '0' && c <= '9'); +} + +bool QScript::Lexer::isHexDigit(ushort c) const +{ + return ((c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F')); +} + +bool QScript::Lexer::isOctalDigit(ushort c) const +{ + return (c >= '0' && c <= '7'); +} + +int QScript::Lexer::matchPunctuator(ushort c1, ushort c2, + ushort c3, ushort c4) +{ + if (c1 == '>' && c2 == '>' && c3 == '>' && c4 == '=') { + shift(4); + return QScriptGrammar::T_GT_GT_GT_EQ; + } else if (c1 == '=' && c2 == '=' && c3 == '=') { + shift(3); + return QScriptGrammar::T_EQ_EQ_EQ; + } else if (c1 == '!' && c2 == '=' && c3 == '=') { + shift(3); + return QScriptGrammar::T_NOT_EQ_EQ; + } else if (c1 == '>' && c2 == '>' && c3 == '>') { + shift(3); + return QScriptGrammar::T_GT_GT_GT; + } else if (c1 == '<' && c2 == '<' && c3 == '=') { + shift(3); + return QScriptGrammar::T_LT_LT_EQ; + } else if (c1 == '>' && c2 == '>' && c3 == '=') { + shift(3); + return QScriptGrammar::T_GT_GT_EQ; + } else if (c1 == '<' && c2 == '=') { + shift(2); + return QScriptGrammar::T_LE; + } else if (c1 == '>' && c2 == '=') { + shift(2); + return QScriptGrammar::T_GE; + } else if (c1 == '!' && c2 == '=') { + shift(2); + return QScriptGrammar::T_NOT_EQ; + } else if (c1 == '+' && c2 == '+') { + shift(2); + return QScriptGrammar::T_PLUS_PLUS; + } else if (c1 == '-' && c2 == '-') { + shift(2); + return QScriptGrammar::T_MINUS_MINUS; + } else if (c1 == '=' && c2 == '=') { + shift(2); + return QScriptGrammar::T_EQ_EQ; + } else if (c1 == '+' && c2 == '=') { + shift(2); + return QScriptGrammar::T_PLUS_EQ; + } else if (c1 == '-' && c2 == '=') { + shift(2); + return QScriptGrammar::T_MINUS_EQ; + } else if (c1 == '*' && c2 == '=') { + shift(2); + return QScriptGrammar::T_STAR_EQ; + } else if (c1 == '/' && c2 == '=') { + shift(2); + return QScriptGrammar::T_DIVIDE_EQ; + } else if (c1 == '&' && c2 == '=') { + shift(2); + return QScriptGrammar::T_AND_EQ; + } else if (c1 == '^' && c2 == '=') { + shift(2); + return QScriptGrammar::T_XOR_EQ; + } else if (c1 == '%' && c2 == '=') { + shift(2); + return QScriptGrammar::T_REMAINDER_EQ; + } else if (c1 == '|' && c2 == '=') { + shift(2); + return QScriptGrammar::T_OR_EQ; + } else if (c1 == '<' && c2 == '<') { + shift(2); + return QScriptGrammar::T_LT_LT; + } else if (c1 == '>' && c2 == '>') { + shift(2); + return QScriptGrammar::T_GT_GT; + } else if (c1 == '&' && c2 == '&') { + shift(2); + return QScriptGrammar::T_AND_AND; + } else if (c1 == '|' && c2 == '|') { + shift(2); + return QScriptGrammar::T_OR_OR; + } + + switch(c1) { + case '=': shift(1); return QScriptGrammar::T_EQ; + case '>': shift(1); return QScriptGrammar::T_GT; + case '<': shift(1); return QScriptGrammar::T_LT; + case ',': shift(1); return QScriptGrammar::T_COMMA; + case '!': shift(1); return QScriptGrammar::T_NOT; + case '~': shift(1); return QScriptGrammar::T_TILDE; + case '?': shift(1); return QScriptGrammar::T_QUESTION; + case ':': shift(1); return QScriptGrammar::T_COLON; + case '.': shift(1); return QScriptGrammar::T_DOT; + case '+': shift(1); return QScriptGrammar::T_PLUS; + case '-': shift(1); return QScriptGrammar::T_MINUS; + case '*': shift(1); return QScriptGrammar::T_STAR; + case '/': shift(1); return QScriptGrammar::T_DIVIDE_; + case '&': shift(1); return QScriptGrammar::T_AND; + case '|': shift(1); return QScriptGrammar::T_OR; + case '^': shift(1); return QScriptGrammar::T_XOR; + case '%': shift(1); return QScriptGrammar::T_REMAINDER; + case '(': shift(1); return QScriptGrammar::T_LPAREN; + case ')': shift(1); return QScriptGrammar::T_RPAREN; + case '{': shift(1); return QScriptGrammar::T_LBRACE; + case '}': shift(1); return QScriptGrammar::T_RBRACE; + case '[': shift(1); return QScriptGrammar::T_LBRACKET; + case ']': shift(1); return QScriptGrammar::T_RBRACKET; + case ';': shift(1); return QScriptGrammar::T_SEMICOLON; + + default: return -1; + } +} + +ushort QScript::Lexer::singleEscape(ushort c) const +{ + switch(c) { + case 'b': + return 0x08; + case 't': + return 0x09; + case 'n': + return 0x0A; + case 'v': + return 0x0B; + case 'f': + return 0x0C; + case 'r': + return 0x0D; + case '"': + return 0x22; + case '\'': + return 0x27; + case '\\': + return 0x5C; + default: + return c; + } +} + +ushort QScript::Lexer::convertOctal(ushort c1, ushort c2, + ushort c3) const +{ + return ((c1 - '0') * 64 + (c2 - '0') * 8 + c3 - '0'); +} + +unsigned char QScript::Lexer::convertHex(ushort c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else + return (c - 'A' + 10); +} + +unsigned char QScript::Lexer::convertHex(ushort c1, ushort c2) +{ + return ((convertHex(c1) << 4) + convertHex(c2)); +} + +QChar QScript::Lexer::convertUnicode(ushort c1, ushort c2, + ushort c3, ushort c4) +{ + return QChar((convertHex(c3) << 4) + convertHex(c4), + (convertHex(c1) << 4) + convertHex(c2)); +} + +void QScript::Lexer::record8(ushort c) +{ + Q_ASSERT(c <= 0xff); + + // enlarge buffer if full + if (pos8 >= size8 - 1) { + char *tmp = new char[2 * size8]; + memcpy(tmp, buffer8, size8 * sizeof(char)); + delete [] buffer8; + buffer8 = tmp; + size8 *= 2; + } + + buffer8[pos8++] = (char) c; +} + +void QScript::Lexer::record16(QChar c) +{ + // enlarge buffer if full + if (pos16 >= size16 - 1) { + QChar *tmp = new QChar[2 * size16]; + memcpy(tmp, buffer16, size16 * sizeof(QChar)); + delete [] buffer16; + buffer16 = tmp; + size16 *= 2; + } + + buffer16[pos16++] = c; +} + +void QScript::Lexer::recordStartPos() +{ + startlineno = yylineno; + startcolumn = yycolumn; +} + +bool QScript::Lexer::scanRegExp(RegExpBodyPrefix prefix) +{ + pos16 = 0; + bool lastWasEscape = false; + + if (prefix == EqualPrefix) + record16(QLatin1Char('=')); + + while (1) { + if (isLineTerminator() || current == 0) { + errmsg = QLatin1String("Unterminated regular expression literal"); + return false; + } + else if (current != '/' || lastWasEscape == true) + { + record16(current); + lastWasEscape = !lastWasEscape && (current == '\\'); + } + else { + pattern = QString(buffer16, pos16); + pos16 = 0; + shift(1); + break; + } + shift(1); + } + + flags = 0; + while (isIdentLetter(current)) { + record16(current); + shift(1); + } + + return true; +} + +void QScript::Lexer::syncProhibitAutomaticSemicolon() +{ + if (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; + } else { + prohibitAutomaticSemicolon = false; + } +} + + +class Translator; + +class QScriptParser: protected QScriptGrammar +{ +public: + QVariant val; + + struct Location { + int startLine; + int startColumn; + int endLine; + int endColumn; + }; + +public: + QScriptParser(); + ~QScriptParser(); + + bool parse(QScript::Lexer *lexer, + const QString &fileName, + Translator *translator); + + inline QString errorMessage() const + { return error_message; } + inline int errorLineNumber() const + { return error_lineno; } + inline int errorColumnNumber() const + { return error_column; } + +protected: + inline void reallocateStack(); + + inline QVariant &sym(int index) + { return sym_stack [tos + index - 1]; } + + inline Location &loc(int index) + { return location_stack [tos + index - 2]; } + +protected: + int tos; + int stack_size; + QVector<QVariant> sym_stack; + int *state_stack; + Location *location_stack; + QString error_message; + int error_lineno; + int error_column; +}; + +inline void QScriptParser::reallocateStack() +{ + if (! stack_size) + stack_size = 128; + else + stack_size <<= 1; + + sym_stack.resize(stack_size); + state_stack = reinterpret_cast<int*> (qRealloc(state_stack, stack_size * sizeof(int))); + location_stack = reinterpret_cast<Location*> (qRealloc(location_stack, stack_size * sizeof(Location))); +} + +inline static bool automatic(QScript::Lexer *lexer, int token) +{ + return (token == QScriptGrammar::T_RBRACE) + || (token == 0) + || lexer->prevTerminator(); +} + +QScriptParser::QScriptParser(): + tos(0), + stack_size(0), + sym_stack(0), + state_stack(0), + location_stack(0) +{ +} + +QScriptParser::~QScriptParser() +{ + if (stack_size) { + qFree(state_stack); + qFree(location_stack); + } +} + +static inline QScriptParser::Location location(QScript::Lexer *lexer) +{ + QScriptParser::Location loc; + loc.startLine = lexer->startLineNo(); + loc.startColumn = lexer->startColumnNo(); + loc.endLine = lexer->endLineNo(); + loc.endColumn = lexer->endColumnNo(); + return loc; +} + +bool QScriptParser::parse(QScript::Lexer *lexer, + const QString &fileName, + Translator *translator) +{ + const int INITIAL_STATE = 0; + + int yytoken = -1; + int saved_yytoken = -1; + int identLineNo = -1; + + reallocateStack(); + + tos = 0; + state_stack[++tos] = INITIAL_STATE; + + while (true) + { + const int state = state_stack [tos]; + if (yytoken == -1 && - TERMINAL_COUNT != action_index [state]) + { + if (saved_yytoken == -1) + { + yytoken = lexer->lex(); + location_stack [tos] = location(lexer); + } + else + { + yytoken = saved_yytoken; + saved_yytoken = -1; + } + } + + int act = t_action (state, yytoken); + + if (act == ACCEPT_STATE) + return true; + + else if (act > 0) + { + if (++tos == stack_size) + reallocateStack(); + + sym_stack [tos] = lexer->val (); + state_stack [tos] = act; + location_stack [tos] = location(lexer); + yytoken = -1; + } + + else if (act < 0) + { + int r = - act - 1; + + tos -= rhs [r]; + act = state_stack [tos++]; + + switch (r) { + +case 1: { + sym(1) = sym(1).toByteArray(); + identLineNo = lexer->startLineNo(); +} break; + +case 7: { + bool rx = lexer->scanRegExp(QScript::Lexer::NoPrefix); + if (!rx) { + error_message = lexer->errorMessage(); + error_lineno = lexer->startLineNo(); + error_column = lexer->startColumnNo(); + return false; + } +} break; + +case 8: { + bool rx = lexer->scanRegExp(QScript::Lexer::EqualPrefix); + if (!rx) { + error_message = lexer->errorMessage(); + error_lineno = lexer->startLineNo(); + error_column = lexer->startColumnNo(); + return false; + } +} break; + +case 66: { + QString name = sym(1).toString(); + if ((name == QLatin1String("qsTranslate")) || (name == QLatin1String("QT_TRANSLATE_NOOP"))) { + QVariantList args = sym(2).toList(); + if (args.size() < 2) { + qWarning("%s:%d: %s() requires at least two arguments", + qPrintable(fileName), identLineNo, qPrintable(name)); + } else { + if ((args.at(0).type() != QVariant::String) + || (args.at(1).type() != QVariant::String)) { + qWarning("%s:%d: %s(): both arguments must be literal strings", + qPrintable(fileName), identLineNo, qPrintable(name)); + } else { + QString context = args.at(0).toString(); + QString text = args.at(1).toString(); + QString comment = args.value(2).toString(); + QString extracomment; + bool plural = (args.size() > 4); + recordMessage(translator, context, text, comment, extracomment, + plural, fileName, identLineNo); + } + } + } else if ((name == QLatin1String("qsTr")) || (name == QLatin1String("QT_TR_NOOP"))) { + QVariantList args = sym(2).toList(); + if (args.size() < 1) { + qWarning("%s:%d: %s() requires at least one argument", + qPrintable(fileName), identLineNo, qPrintable(name)); + } else { + if (args.at(0).type() != QVariant::String) { + qWarning("%s:%d: %s(): text to translate must be a literal string", + qPrintable(fileName), identLineNo, qPrintable(name)); + } else { + QString context = QFileInfo(fileName).baseName(); + QString text = args.at(0).toString(); + QString comment = args.value(1).toString(); + QString extracomment; + bool plural = (args.size() > 2); + recordMessage(translator, context, text, comment, extracomment, + plural, fileName, identLineNo); + } + } + } +} break; + +case 70: { + sym(1) = QVariantList(); +} break; + +case 71: { + sym(1) = sym(2); +} break; + +case 72: { + sym(1) = QVariantList() << sym(1); +} break; + +case 73: { + sym(1) = sym(1).toList() << sym(3); +} break; + +case 94: { + if ((sym(1).type() == QVariant::String) || (sym(3).type() == QVariant::String)) + sym(1) = sym(1).toString() + sym(3).toString(); + else + sym(1) = QVariant(); +} break; + + } // switch + + state_stack [tos] = nt_action (act, lhs [r] - TERMINAL_COUNT); + + if (rhs[r] > 1) { + location_stack[tos - 1].endLine = location_stack[tos + rhs[r] - 2].endLine; + location_stack[tos - 1].endColumn = location_stack[tos + rhs[r] - 2].endColumn; + location_stack[tos] = location_stack[tos + rhs[r] - 1]; + } + } + + else + { + if (saved_yytoken == -1 && automatic (lexer, yytoken) && t_action (state, T_AUTOMATIC_SEMICOLON) > 0) + { + saved_yytoken = yytoken; + yytoken = T_SEMICOLON; + continue; + } + + else if ((state == INITIAL_STATE) && (yytoken == 0)) { + // accept empty input + yytoken = T_SEMICOLON; + continue; + } + + int ers = state; + int shifts = 0; + int reduces = 0; + int expected_tokens [3]; + for (int tk = 0; tk < TERMINAL_COUNT; ++tk) + { + int k = t_action (ers, tk); + + if (! k) + continue; + else if (k < 0) + ++reduces; + else if (spell [tk]) + { + if (shifts < 3) + expected_tokens [shifts] = tk; + ++shifts; + } + } + + error_message.clear (); + if (shifts && shifts < 3) + { + bool first = true; + + for (int s = 0; s < shifts; ++s) + { + if (first) + error_message += QLatin1String ("Expected "); + else + error_message += QLatin1String (", "); + + first = false; + error_message += QLatin1String("`"); + error_message += QLatin1String (spell [expected_tokens [s]]); + error_message += QLatin1String("'"); + } + } + + if (error_message.isEmpty()) + error_message = lexer->errorMessage(); + + error_lineno = lexer->startLineNo(); + error_column = lexer->startColumnNo(); + + return false; + } + } + + return false; +} + + +bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd) +{ + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + return false; + } + QTextStream ts(&file); + QByteArray codecName; + if (!cd.m_codecForSource.isEmpty()) + codecName = cd.m_codecForSource; + else + codecName = translator.codecName(); // Just because it should be latin1 already + ts.setCodec(QTextCodec::codecForName(codecName)); + ts.setAutoDetectUnicode(true); + + QString code = ts.readAll(); + QScript::Lexer lexer; + lexer.setCode(code, /*lineNumber=*/1); + QScriptParser parser; + if (!parser.parse(&lexer, filename, &translator)) { + qWarning("%s:%d: %s", qPrintable(filename), parser.errorLineNumber(), + qPrintable(parser.errorMessage())); + return false; + } + + // Java uses UTF-16 internally and Jambi makes UTF-8 for tr() purposes of it. + translator.setCodecName("UTF-8"); + return true; +} + +QT_END_NAMESPACE diff --git a/tools/linguist/lupdate/qscript.g b/tools/linguist/lupdate/qscript.g new file mode 100644 index 0000000..4e36395 --- /dev/null +++ b/tools/linguist/lupdate/qscript.g @@ -0,0 +1,2026 @@ +---------------------------------------------------------------------------- +-- +-- Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +-- Contact: Nokia Corporation (qt-info@nokia.com) +-- +-- This file is part of the Qt Linguist of the Qt Toolkit. +-- +-- $QT_BEGIN_LICENSE:LGPL$ +-- No Commercial Usage +-- This file contains pre-release code and may not be distributed. +-- You may use this file in accordance with the terms and conditions +-- contained in the either Technology Preview License Agreement or the +-- Beta Release License Agreement. +-- +-- GNU Lesser General Public License Usage +-- Alternatively, this file may be used under the terms of the GNU Lesser +-- General Public License version 2.1 as published by the Free Software +-- Foundation and appearing in the file LICENSE.LGPL included in the +-- packaging of this file. Please review the following information to +-- ensure the GNU Lesser General Public License version 2.1 requirements +-- will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +-- +-- In addition, as a special exception, Nokia gives you certain +-- additional rights. These rights are described in the Nokia Qt LGPL +-- Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +-- package. +-- +-- GNU General Public License Usage +-- Alternatively, this file may be used under the terms of the GNU +-- General Public License version 3.0 as published by the Free Software +-- Foundation and appearing in the file LICENSE.GPL included in the +-- packaging of this file. Please review the following information to +-- ensure the GNU General Public License version 3.0 requirements will be +-- met: http://www.gnu.org/copyleft/gpl.html. +-- +-- If you are unsure which license is appropriate for your use, please +-- contact the sales department at http://www.qtsoftware.com/contact. +-- $QT_END_LICENSE$ +-- +-- This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +-- WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +-- +---------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- Process with "qlalr --no-debug --no-lines qscript.g" to update qscript.cpp -- +-------------------------------------------------------------------------------- + +%parser QScriptGrammar +%merged_output qscript.cpp +%expect 3 +%expect-rr 1 + +%token T_AND "&" T_AND_AND "&&" T_AND_EQ "&=" +%token T_BREAK "break" T_CASE "case" T_CATCH "catch" +%token T_COLON ":" T_COMMA ";" T_CONTINUE "continue" +%token T_DEFAULT "default" T_DELETE "delete" T_DIVIDE_ "/" +%token T_DIVIDE_EQ "/=" T_DO "do" T_DOT "." +%token T_ELSE "else" T_EQ "=" T_EQ_EQ "==" +%token T_EQ_EQ_EQ "===" T_FINALLY "finally" T_FOR "for" +%token T_FUNCTION "function" T_GE ">=" T_GT ">" +%token T_GT_GT ">>" T_GT_GT_EQ ">>=" T_GT_GT_GT ">>>" +%token T_GT_GT_GT_EQ ">>>=" T_IDENTIFIER "identifier" T_IF "if" +%token T_IN "in" T_INSTANCEOF "instanceof" T_LBRACE "{" +%token T_LBRACKET "[" T_LE "<=" T_LPAREN "(" +%token T_LT "<" T_LT_LT "<<" T_LT_LT_EQ "<<=" +%token T_MINUS "-" T_MINUS_EQ "-=" T_MINUS_MINUS "--" +%token T_NEW "new" T_NOT "!" T_NOT_EQ "!=" +%token T_NOT_EQ_EQ "!==" T_NUMERIC_LITERAL "numeric literal" T_OR "|" +%token T_OR_EQ "|=" T_OR_OR "||" T_PLUS "+" +%token T_PLUS_EQ "+=" T_PLUS_PLUS "++" T_QUESTION "?" +%token T_RBRACE "}" T_RBRACKET "]" T_REMAINDER "%" +%token T_REMAINDER_EQ "%=" T_RETURN "return" T_RPAREN ")" +%token T_SEMICOLON ";" T_AUTOMATIC_SEMICOLON T_STAR "*" +%token T_STAR_EQ "*=" T_STRING_LITERAL "string literal" +%token T_SWITCH "switch" T_THIS "this" T_THROW "throw" +%token T_TILDE "~" T_TRY "try" T_TYPEOF "typeof" +%token T_VAR "var" T_VOID "void" T_WHILE "while" +%token T_WITH "with" T_XOR "^" T_XOR_EQ "^=" +%token T_NULL "null" T_TRUE "true" T_FALSE "false" +%token T_CONST "const" +%token T_DEBUGGER "debugger" +%token T_RESERVED_WORD "reserved word" + +%start Program + +/. +#include <translator.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qnumeric.h> +#include <QtCore/qstring.h> +#include <QtCore/qtextcodec.h> +#include <QtCore/qvariant.h> + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +QT_BEGIN_NAMESPACE + +static void recordMessage( + Translator *tor, const QString &context, const QString &text, const QString &comment, + const QString &extracomment, bool plural, const QString &fileName, int lineNo) +{ + TranslatorMessage msg( + context, text, comment, QString(), + fileName, lineNo, QStringList(), + TranslatorMessage::Unfinished, plural); + msg.setExtraComment(extracomment.simplified()); + tor->replace(msg); +} + + +namespace QScript +{ + +class Lexer +{ +public: + Lexer(); + ~Lexer(); + + void setCode(const QString &c, int lineno); + int lex(); + + int currentLineNo() const { return yylineno; } + int currentColumnNo() const { return yycolumn; } + + int startLineNo() const { return startlineno; } + int startColumnNo() const { return startcolumn; } + + int endLineNo() const { return currentLineNo(); } + int endColumnNo() const + { int col = currentColumnNo(); return (col > 0) ? col - 1 : col; } + + bool prevTerminator() const { return terminator; } + + enum State { Start, + Identifier, + InIdentifier, + InSingleLineComment, + InMultiLineComment, + InNum, + InNum0, + InHex, + InOctal, + InDecimal, + InExponentIndicator, + InExponent, + Hex, + Octal, + Number, + String, + Eof, + InString, + InEscapeSequence, + InHexEscape, + InUnicodeEscape, + Other, + Bad }; + + enum Error { + NoError, + IllegalCharacter, + UnclosedStringLiteral, + IllegalEscapeSequence, + IllegalUnicodeEscapeSequence, + UnclosedComment, + IllegalExponentIndicator, + IllegalIdentifier + }; + + enum ParenthesesState { + IgnoreParentheses, + CountParentheses, + BalancedParentheses + }; + + enum RegExpBodyPrefix { + NoPrefix, + EqualPrefix + }; + + bool scanRegExp(RegExpBodyPrefix prefix = NoPrefix); + + QString pattern; + int flags; + + State lexerState() const + { return state; } + + QString errorMessage() const + { return errmsg; } + void setErrorMessage(const QString &err) + { errmsg = err; } + void setErrorMessage(const char *err) + { setErrorMessage(QString::fromLatin1(err)); } + + Error error() const + { return err; } + void clearError() + { err = NoError; } + +private: + int yylineno; + bool done; + char *buffer8; + QChar *buffer16; + uint size8, size16; + uint pos8, pos16; + bool terminator; + bool restrKeyword; + // encountered delimiter like "'" and "}" on last run + bool delimited; + int stackToken; + + State state; + void setDone(State s); + uint pos; + void shift(uint p); + int lookupKeyword(const char *); + + bool isWhiteSpace() const; + bool isLineTerminator() const; + bool isHexDigit(ushort c) const; + bool isOctalDigit(ushort c) const; + + int matchPunctuator(ushort c1, ushort c2, + ushort c3, ushort c4); + ushort singleEscape(ushort c) const; + ushort convertOctal(ushort c1, ushort c2, + ushort c3) const; +public: + static unsigned char convertHex(ushort c1); + static unsigned char convertHex(ushort c1, ushort c2); + static QChar convertUnicode(ushort c1, ushort c2, + ushort c3, ushort c4); + static bool isIdentLetter(ushort c); + static bool isDecimalDigit(ushort c); + + inline int ival() const { return qsyylval.toInt(); } + inline double dval() const { return qsyylval.toDouble(); } + inline QString ustr() const { return qsyylval.toString(); } + inline QVariant val() const { return qsyylval; } + + const QChar *characterBuffer() const { return buffer16; } + int characterCount() const { return pos16; } + +private: + void record8(ushort c); + void record16(QChar c); + void recordStartPos(); + + int findReservedWord(const QChar *buffer, int size) const; + + void syncProhibitAutomaticSemicolon(); + + const QChar *code; + uint length; + int yycolumn; + int startlineno; + int startcolumn; + int bol; // begin of line + + QVariant qsyylval; + + // current and following unicode characters + ushort current, next1, next2, next3; + + struct keyword { + const char *name; + int token; + }; + + QString errmsg; + Error err; + + bool wantRx; + bool check_reserved; + + ParenthesesState parenthesesState; + int parenthesesCount; + bool prohibitAutomaticSemicolon; +}; + +} // namespace QScript + +extern double qstrtod(const char *s00, char const **se, bool *ok); + +#define shiftWindowsLineBreak() if(current == '\r' && next1 == '\n') shift(1); + +namespace QScript { + +static 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 qSNaN(); + + 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 = qSNaN(); + } else { + result = 0; + double multiplier = 1; + for (--i ; i >= j; --i, multiplier *= radix) + result += toDigit(buf[i]) * multiplier; + } + result *= sign; + return result; +} + +} // namespace QScript + +QScript::Lexer::Lexer() + : + yylineno(0), + size8(128), size16(128), restrKeyword(false), + stackToken(-1), pos(0), + code(0), length(0), + bol(true), + current(0), next1(0), next2(0), next3(0), + err(NoError), + check_reserved(true), + parenthesesState(IgnoreParentheses), + prohibitAutomaticSemicolon(false) +{ + // allocate space for read buffers + buffer8 = new char[size8]; + buffer16 = new QChar[size16]; + flags = 0; + +} + +QScript::Lexer::~Lexer() +{ + delete [] buffer8; + delete [] buffer16; +} + +void QScript::Lexer::setCode(const QString &c, int lineno) +{ + errmsg = QString(); + yylineno = lineno; + yycolumn = 1; + restrKeyword = false; + delimited = false; + stackToken = -1; + pos = 0; + code = c.unicode(); + length = c.length(); + bol = true; + + // read first characters + current = (length > 0) ? code[0].unicode() : 0; + next1 = (length > 1) ? code[1].unicode() : 0; + next2 = (length > 2) ? code[2].unicode() : 0; + next3 = (length > 3) ? code[3].unicode() : 0; +} + +void QScript::Lexer::shift(uint p) +{ + while (p--) { + ++pos; + ++yycolumn; + current = next1; + next1 = next2; + next2 = next3; + next3 = (pos + 3 < length) ? code[pos+3].unicode() : 0; + } +} + +void QScript::Lexer::setDone(State s) +{ + state = s; + done = true; +} + +int QScript::Lexer::findReservedWord(const QChar *c, int size) const +{ + switch (size) { + case 2: { + if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('o')) + return QScriptGrammar::T_DO; + else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('f')) + return QScriptGrammar::T_IF; + else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n')) + return QScriptGrammar::T_IN; + } break; + + case 3: { + if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('o') && c[2] == QLatin1Char('r')) + return QScriptGrammar::T_FOR; + else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('e') && c[2] == QLatin1Char('w')) + return QScriptGrammar::T_NEW; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') && c[2] == QLatin1Char('y')) + return QScriptGrammar::T_TRY; + else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('a') && c[2] == QLatin1Char('r')) + return QScriptGrammar::T_VAR; + else if (check_reserved) { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') && c[2] == QLatin1Char('t')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + case 4: { + if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e')) + return QScriptGrammar::T_CASE; + else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('l') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('e')) + return QScriptGrammar::T_ELSE; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('s')) + return QScriptGrammar::T_THIS; + else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('d')) + return QScriptGrammar::T_VOID; + else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('i') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('h')) + return QScriptGrammar::T_WITH; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('e')) + return QScriptGrammar::T_TRUE; + else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('l')) + return QScriptGrammar::T_NULL; + else if (check_reserved) { + if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('n') + && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('m')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('y') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('l') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('g')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('r')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('g') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('o')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + case 5: { + if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('e') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('k')) + return QScriptGrammar::T_BREAK; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('c') + && c[4] == QLatin1Char('h')) + return QScriptGrammar::T_CATCH; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('r') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('w')) + return QScriptGrammar::T_THROW; + else if (c[0] == QLatin1Char('w') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('l') + && c[4] == QLatin1Char('e')) + return QScriptGrammar::T_WHILE; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('s') + && c[4] == QLatin1Char('t')) + return QScriptGrammar::T_CONST; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('s') + && c[4] == QLatin1Char('e')) + return QScriptGrammar::T_FALSE; + else if (check_reserved) { + if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('r') + && c[4] == QLatin1Char('t')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('r')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('i') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('l')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('l') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('s') + && c[4] == QLatin1Char('s')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('l') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('t')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + case 6: { + if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('e')) + return QScriptGrammar::T_DELETE; + else if (c[0] == QLatin1Char('r') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('u') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('n')) + return QScriptGrammar::T_RETURN; + else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('w') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('c') && c[5] == QLatin1Char('h')) + return QScriptGrammar::T_SWITCH; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('y') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('o') && c[5] == QLatin1Char('f')) + return QScriptGrammar::T_TYPEOF; + else if (check_reserved) { + if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('x') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('t') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('c')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('u') && c[3] == QLatin1Char('b') + && c[4] == QLatin1Char('l') && c[5] == QLatin1Char('e')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('t')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('b') && c[3] == QLatin1Char('l') + && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('c')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('n') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('i') + && c[4] == QLatin1Char('v') && c[5] == QLatin1Char('e')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('h') + && c[2] == QLatin1Char('r') && c[3] == QLatin1Char('o') + && c[4] == QLatin1Char('w') && c[5] == QLatin1Char('s')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + case 7: { + if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('f') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('u') && c[5] == QLatin1Char('l') + && c[6] == QLatin1Char('t')) + return QScriptGrammar::T_DEFAULT; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('i') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('l') && c[5] == QLatin1Char('l') + && c[6] == QLatin1Char('y')) + return QScriptGrammar::T_FINALLY; + else if (check_reserved) { + if (c[0] == QLatin1Char('b') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('l') + && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('a') + && c[6] == QLatin1Char('n')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('e') && c[1] == QLatin1Char('x') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('n') && c[5] == QLatin1Char('d') + && c[6] == QLatin1Char('s')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('a') + && c[2] == QLatin1Char('c') && c[3] == QLatin1Char('k') + && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('g') + && c[6] == QLatin1Char('e')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('i') && c[3] == QLatin1Char('v') + && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('t') + && c[6] == QLatin1Char('e')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + case 8: { + if (c[0] == QLatin1Char('c') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('i') && c[5] == QLatin1Char('n') + && c[6] == QLatin1Char('u') && c[7] == QLatin1Char('e')) + return QScriptGrammar::T_CONTINUE; + else if (c[0] == QLatin1Char('f') && c[1] == QLatin1Char('u') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('c') + && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('i') + && c[6] == QLatin1Char('o') && c[7] == QLatin1Char('n')) + return QScriptGrammar::T_FUNCTION; + else if (c[0] == QLatin1Char('d') && c[1] == QLatin1Char('e') + && c[2] == QLatin1Char('b') && c[3] == QLatin1Char('u') + && c[4] == QLatin1Char('g') && c[5] == QLatin1Char('g') + && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('r')) + return QScriptGrammar::T_DEBUGGER; + else if (check_reserved) { + if (c[0] == QLatin1Char('a') && c[1] == QLatin1Char('b') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('a') + && c[6] == QLatin1Char('c') && c[7] == QLatin1Char('t')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('v') && c[1] == QLatin1Char('o') + && c[2] == QLatin1Char('l') && c[3] == QLatin1Char('a') + && c[4] == QLatin1Char('t') && c[5] == QLatin1Char('i') + && c[6] == QLatin1Char('l') && c[7] == QLatin1Char('e')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + case 9: { + if (check_reserved) { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') + && c[2] == QLatin1Char('t') && c[3] == QLatin1Char('e') + && c[4] == QLatin1Char('r') && c[5] == QLatin1Char('f') + && c[6] == QLatin1Char('a') && c[7] == QLatin1Char('c') + && c[8] == QLatin1Char('e')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('t') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('a') && c[3] == QLatin1Char('n') + && c[4] == QLatin1Char('s') && c[5] == QLatin1Char('i') + && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('n') + && c[8] == QLatin1Char('t')) + return QScriptGrammar::T_RESERVED_WORD; + else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r') + && c[2] == QLatin1Char('o') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('c') + && c[6] == QLatin1Char('t') && c[7] == QLatin1Char('e') + && c[8] == QLatin1Char('d')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + case 10: { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('n') + && c[2] == QLatin1Char('s') && c[3] == QLatin1Char('t') + && c[4] == QLatin1Char('a') && c[5] == QLatin1Char('n') + && c[6] == QLatin1Char('c') && c[7] == QLatin1Char('e') + && c[8] == QLatin1Char('o') && c[9] == QLatin1Char('f')) + return QScriptGrammar::T_INSTANCEOF; + else if (check_reserved) { + if (c[0] == QLatin1Char('i') && c[1] == QLatin1Char('m') + && c[2] == QLatin1Char('p') && c[3] == QLatin1Char('l') + && c[4] == QLatin1Char('e') && c[5] == QLatin1Char('m') + && c[6] == QLatin1Char('e') && c[7] == QLatin1Char('n') + && c[8] == QLatin1Char('t') && c[9] == QLatin1Char('s')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + case 12: { + if (check_reserved) { + if (c[0] == QLatin1Char('s') && c[1] == QLatin1Char('y') + && c[2] == QLatin1Char('n') && c[3] == QLatin1Char('c') + && c[4] == QLatin1Char('h') && c[5] == QLatin1Char('r') + && c[6] == QLatin1Char('o') && c[7] == QLatin1Char('n') + && c[8] == QLatin1Char('i') && c[9] == QLatin1Char('z') + && c[10] == QLatin1Char('e') && c[11] == QLatin1Char('d')) + return QScriptGrammar::T_RESERVED_WORD; + } + } break; + + } // switch + + return -1; +} + +int QScript::Lexer::lex() +{ + int token = 0; + state = Start; + ushort stringType = 0; // either single or double quotes + pos8 = pos16 = 0; + done = false; + terminator = false; + + // did we push a token on the stack previously ? + // (after an automatic semicolon insertion) + if (stackToken >= 0) { + setDone(Other); + token = stackToken; + stackToken = -1; + } + + while (!done) { + switch (state) { + case Start: + if (isWhiteSpace()) { + // do nothing + } else if (current == '/' && next1 == '/') { + recordStartPos(); + shift(1); + state = InSingleLineComment; + } else if (current == '/' && next1 == '*') { + recordStartPos(); + shift(1); + state = InMultiLineComment; + } else if (current == 0) { + syncProhibitAutomaticSemicolon(); + if (!terminator && !delimited && !prohibitAutomaticSemicolon) { + // automatic semicolon insertion if program incomplete + token = QScriptGrammar::T_SEMICOLON; + stackToken = 0; + setDone(Other); + } else { + setDone(Eof); + } + } else if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + yycolumn = 0; + bol = true; + terminator = true; + syncProhibitAutomaticSemicolon(); + if (restrKeyword) { + token = QScriptGrammar::T_SEMICOLON; + setDone(Other); + } + } else if (current == '"' || current == '\'') { + recordStartPos(); + state = InString; + stringType = current; + } else if (isIdentLetter(current)) { + recordStartPos(); + record16(current); + state = InIdentifier; + } else if (current == '0') { + recordStartPos(); + record8(current); + state = InNum0; + } else if (isDecimalDigit(current)) { + recordStartPos(); + record8(current); + state = InNum; + } else if (current == '.' && isDecimalDigit(next1)) { + recordStartPos(); + record8(current); + state = InDecimal; + } else { + recordStartPos(); + token = matchPunctuator(current, next1, next2, next3); + if (token != -1) { + if (terminator && !delimited && !prohibitAutomaticSemicolon + && (token == QScriptGrammar::T_PLUS_PLUS + || token == QScriptGrammar::T_MINUS_MINUS)) { + // automatic semicolon insertion + stackToken = token; + token = QScriptGrammar::T_SEMICOLON; + } + setDone(Other); + } + else { + setDone(Bad); + err = IllegalCharacter; + errmsg = QLatin1String("Illegal character"); + } + } + break; + case InString: + if (current == stringType) { + shift(1); + setDone(String); + } else if (current == 0 || isLineTerminator()) { + setDone(Bad); + err = UnclosedStringLiteral; + errmsg = QLatin1String("Unclosed string at end of line"); + } else if (current == '\\') { + state = InEscapeSequence; + } else { + record16(current); + } + break; + // Escape Sequences inside of strings + case InEscapeSequence: + if (isOctalDigit(current)) { + if (current >= '0' && current <= '3' && + isOctalDigit(next1) && isOctalDigit(next2)) { + record16(convertOctal(current, next1, next2)); + shift(2); + state = InString; + } else if (isOctalDigit(current) && + isOctalDigit(next1)) { + record16(convertOctal('0', current, next1)); + shift(1); + state = InString; + } else if (isOctalDigit(current)) { + record16(convertOctal('0', '0', current)); + state = InString; + } else { + setDone(Bad); + err = IllegalEscapeSequence; + errmsg = QLatin1String("Illegal escape squence"); + } + } else if (current == 'x') + state = InHexEscape; + else if (current == 'u') + state = InUnicodeEscape; + else { + record16(singleEscape(current)); + state = InString; + } + break; + case InHexEscape: + if (isHexDigit(current) && isHexDigit(next1)) { + state = InString; + record16(QLatin1Char(convertHex(current, next1))); + shift(1); + } else if (current == stringType) { + record16(QLatin1Char('x')); + shift(1); + setDone(String); + } else { + record16(QLatin1Char('x')); + record16(current); + state = InString; + } + break; + case InUnicodeEscape: + if (isHexDigit(current) && isHexDigit(next1) && + isHexDigit(next2) && isHexDigit(next3)) { + record16(convertUnicode(current, next1, next2, next3)); + shift(3); + state = InString; + } else if (current == stringType) { + record16(QLatin1Char('u')); + shift(1); + setDone(String); + } else { + setDone(Bad); + err = IllegalUnicodeEscapeSequence; + errmsg = QLatin1String("Illegal unicode escape sequence"); + } + break; + case InSingleLineComment: + if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + yycolumn = 0; + terminator = true; + bol = true; + if (restrKeyword) { + token = QScriptGrammar::T_SEMICOLON; + setDone(Other); + } else + state = Start; + } else if (current == 0) { + setDone(Eof); + } + break; + case InMultiLineComment: + if (current == 0) { + setDone(Bad); + err = UnclosedComment; + errmsg = QLatin1String("Unclosed comment at end of file"); + } else if (isLineTerminator()) { + shiftWindowsLineBreak(); + yylineno++; + } else if (current == '*' && next1 == '/') { + state = Start; + shift(1); + } + break; + case InIdentifier: + if (isIdentLetter(current) || isDecimalDigit(current)) { + record16(current); + break; + } + setDone(Identifier); + break; + case InNum0: + if (current == 'x' || current == 'X') { + record8(current); + state = InHex; + } else if (current == '.') { + record8(current); + state = InDecimal; + } else if (current == 'e' || current == 'E') { + record8(current); + state = InExponentIndicator; + } else if (isOctalDigit(current)) { + record8(current); + state = InOctal; + } else if (isDecimalDigit(current)) { + record8(current); + state = InDecimal; + } else { + setDone(Number); + } + break; + case InHex: + if (isHexDigit(current)) + record8(current); + else + setDone(Hex); + break; + case InOctal: + if (isOctalDigit(current)) { + record8(current); + } else if (isDecimalDigit(current)) { + record8(current); + state = InDecimal; + } else { + setDone(Octal); + } + break; + case InNum: + if (isDecimalDigit(current)) { + record8(current); + } else if (current == '.') { + record8(current); + state = InDecimal; + } else if (current == 'e' || current == 'E') { + record8(current); + state = InExponentIndicator; + } else { + setDone(Number); + } + break; + case InDecimal: + if (isDecimalDigit(current)) { + record8(current); + } else if (current == 'e' || current == 'E') { + record8(current); + state = InExponentIndicator; + } else { + setDone(Number); + } + break; + case InExponentIndicator: + if (current == '+' || current == '-') { + record8(current); + } else if (isDecimalDigit(current)) { + record8(current); + state = InExponent; + } else { + setDone(Bad); + err = IllegalExponentIndicator; + errmsg = QLatin1String("Illegal syntax for exponential number"); + } + break; + case InExponent: + if (isDecimalDigit(current)) { + record8(current); + } else { + setDone(Number); + } + break; + default: + Q_ASSERT_X(0, "Lexer::lex", "Unhandled state in switch statement"); + } + + // move on to the next character + if (!done) + shift(1); + if (state != Start && state != InSingleLineComment) + bol = false; + } + + // no identifiers allowed directly after numeric literal, e.g. "3in" is bad + if ((state == Number || state == Octal || state == Hex) + && isIdentLetter(current)) { + state = Bad; + err = IllegalIdentifier; + errmsg = QLatin1String("Identifier cannot start with numeric literal"); + } + + // terminate string + buffer8[pos8] = '\0'; + + double dval = 0; + if (state == Number) { + dval = qstrtod(buffer8, 0, 0); + } else if (state == Hex) { // scan hex numbers + dval = QScript::integerFromString(buffer8, pos8, 16); + state = Number; + } else if (state == Octal) { // scan octal number + dval = QScript::integerFromString(buffer8, pos8, 8); + state = Number; + } + + restrKeyword = false; + delimited = false; + + switch (parenthesesState) { + case IgnoreParentheses: + break; + case CountParentheses: + if (token == QScriptGrammar::T_RPAREN) { + --parenthesesCount; + if (parenthesesCount == 0) + parenthesesState = BalancedParentheses; + } else if (token == QScriptGrammar::T_LPAREN) { + ++parenthesesCount; + } + break; + case BalancedParentheses: + parenthesesState = IgnoreParentheses; + break; + } + + switch (state) { + case Eof: + return 0; + case Other: + if(token == QScriptGrammar::T_RBRACE || token == QScriptGrammar::T_SEMICOLON) + delimited = true; + return token; + case Identifier: + if ((token = findReservedWord(buffer16, pos16)) < 0) { + /* TODO: close leak on parse error. same holds true for String */ + qsyylval = QString(buffer16, pos16); + return QScriptGrammar::T_IDENTIFIER; + } + if (token == QScriptGrammar::T_CONTINUE || token == QScriptGrammar::T_BREAK + || token == QScriptGrammar::T_RETURN || token == QScriptGrammar::T_THROW) { + restrKeyword = true; + } else if (token == QScriptGrammar::T_IF || token == QScriptGrammar::T_FOR + || token == QScriptGrammar::T_WHILE || token == QScriptGrammar::T_WITH) { + parenthesesState = CountParentheses; + parenthesesCount = 0; + } else if (token == QScriptGrammar::T_DO) { + parenthesesState = BalancedParentheses; + } + return token; + case String: + qsyylval = QString(buffer16, pos16); + return QScriptGrammar::T_STRING_LITERAL; + case Number: + qsyylval = dval; + return QScriptGrammar::T_NUMERIC_LITERAL; + case Bad: + return -1; + default: + Q_ASSERT(!"unhandled numeration value in switch"); + return -1; + } +} + +bool QScript::Lexer::isWhiteSpace() const +{ + return (current == ' ' || current == '\t' || + current == 0x0b || current == 0x0c); +} + +bool QScript::Lexer::isLineTerminator() const +{ + return (current == '\n' || current == '\r'); +} + +bool QScript::Lexer::isIdentLetter(ushort c) +{ + /* TODO: allow other legitimate unicode chars */ + return ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || c == '$' + || c == '_'); +} + +bool QScript::Lexer::isDecimalDigit(ushort c) +{ + return (c >= '0' && c <= '9'); +} + +bool QScript::Lexer::isHexDigit(ushort c) const +{ + return ((c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F')); +} + +bool QScript::Lexer::isOctalDigit(ushort c) const +{ + return (c >= '0' && c <= '7'); +} + +int QScript::Lexer::matchPunctuator(ushort c1, ushort c2, + ushort c3, ushort c4) +{ + if (c1 == '>' && c2 == '>' && c3 == '>' && c4 == '=') { + shift(4); + return QScriptGrammar::T_GT_GT_GT_EQ; + } else if (c1 == '=' && c2 == '=' && c3 == '=') { + shift(3); + return QScriptGrammar::T_EQ_EQ_EQ; + } else if (c1 == '!' && c2 == '=' && c3 == '=') { + shift(3); + return QScriptGrammar::T_NOT_EQ_EQ; + } else if (c1 == '>' && c2 == '>' && c3 == '>') { + shift(3); + return QScriptGrammar::T_GT_GT_GT; + } else if (c1 == '<' && c2 == '<' && c3 == '=') { + shift(3); + return QScriptGrammar::T_LT_LT_EQ; + } else if (c1 == '>' && c2 == '>' && c3 == '=') { + shift(3); + return QScriptGrammar::T_GT_GT_EQ; + } else if (c1 == '<' && c2 == '=') { + shift(2); + return QScriptGrammar::T_LE; + } else if (c1 == '>' && c2 == '=') { + shift(2); + return QScriptGrammar::T_GE; + } else if (c1 == '!' && c2 == '=') { + shift(2); + return QScriptGrammar::T_NOT_EQ; + } else if (c1 == '+' && c2 == '+') { + shift(2); + return QScriptGrammar::T_PLUS_PLUS; + } else if (c1 == '-' && c2 == '-') { + shift(2); + return QScriptGrammar::T_MINUS_MINUS; + } else if (c1 == '=' && c2 == '=') { + shift(2); + return QScriptGrammar::T_EQ_EQ; + } else if (c1 == '+' && c2 == '=') { + shift(2); + return QScriptGrammar::T_PLUS_EQ; + } else if (c1 == '-' && c2 == '=') { + shift(2); + return QScriptGrammar::T_MINUS_EQ; + } else if (c1 == '*' && c2 == '=') { + shift(2); + return QScriptGrammar::T_STAR_EQ; + } else if (c1 == '/' && c2 == '=') { + shift(2); + return QScriptGrammar::T_DIVIDE_EQ; + } else if (c1 == '&' && c2 == '=') { + shift(2); + return QScriptGrammar::T_AND_EQ; + } else if (c1 == '^' && c2 == '=') { + shift(2); + return QScriptGrammar::T_XOR_EQ; + } else if (c1 == '%' && c2 == '=') { + shift(2); + return QScriptGrammar::T_REMAINDER_EQ; + } else if (c1 == '|' && c2 == '=') { + shift(2); + return QScriptGrammar::T_OR_EQ; + } else if (c1 == '<' && c2 == '<') { + shift(2); + return QScriptGrammar::T_LT_LT; + } else if (c1 == '>' && c2 == '>') { + shift(2); + return QScriptGrammar::T_GT_GT; + } else if (c1 == '&' && c2 == '&') { + shift(2); + return QScriptGrammar::T_AND_AND; + } else if (c1 == '|' && c2 == '|') { + shift(2); + return QScriptGrammar::T_OR_OR; + } + + switch(c1) { + case '=': shift(1); return QScriptGrammar::T_EQ; + case '>': shift(1); return QScriptGrammar::T_GT; + case '<': shift(1); return QScriptGrammar::T_LT; + case ',': shift(1); return QScriptGrammar::T_COMMA; + case '!': shift(1); return QScriptGrammar::T_NOT; + case '~': shift(1); return QScriptGrammar::T_TILDE; + case '?': shift(1); return QScriptGrammar::T_QUESTION; + case ':': shift(1); return QScriptGrammar::T_COLON; + case '.': shift(1); return QScriptGrammar::T_DOT; + case '+': shift(1); return QScriptGrammar::T_PLUS; + case '-': shift(1); return QScriptGrammar::T_MINUS; + case '*': shift(1); return QScriptGrammar::T_STAR; + case '/': shift(1); return QScriptGrammar::T_DIVIDE_; + case '&': shift(1); return QScriptGrammar::T_AND; + case '|': shift(1); return QScriptGrammar::T_OR; + case '^': shift(1); return QScriptGrammar::T_XOR; + case '%': shift(1); return QScriptGrammar::T_REMAINDER; + case '(': shift(1); return QScriptGrammar::T_LPAREN; + case ')': shift(1); return QScriptGrammar::T_RPAREN; + case '{': shift(1); return QScriptGrammar::T_LBRACE; + case '}': shift(1); return QScriptGrammar::T_RBRACE; + case '[': shift(1); return QScriptGrammar::T_LBRACKET; + case ']': shift(1); return QScriptGrammar::T_RBRACKET; + case ';': shift(1); return QScriptGrammar::T_SEMICOLON; + + default: return -1; + } +} + +ushort QScript::Lexer::singleEscape(ushort c) const +{ + switch(c) { + case 'b': + return 0x08; + case 't': + return 0x09; + case 'n': + return 0x0A; + case 'v': + return 0x0B; + case 'f': + return 0x0C; + case 'r': + return 0x0D; + case '"': + return 0x22; + case '\'': + return 0x27; + case '\\': + return 0x5C; + default: + return c; + } +} + +ushort QScript::Lexer::convertOctal(ushort c1, ushort c2, + ushort c3) const +{ + return ((c1 - '0') * 64 + (c2 - '0') * 8 + c3 - '0'); +} + +unsigned char QScript::Lexer::convertHex(ushort c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else + return (c - 'A' + 10); +} + +unsigned char QScript::Lexer::convertHex(ushort c1, ushort c2) +{ + return ((convertHex(c1) << 4) + convertHex(c2)); +} + +QChar QScript::Lexer::convertUnicode(ushort c1, ushort c2, + ushort c3, ushort c4) +{ + return QChar((convertHex(c3) << 4) + convertHex(c4), + (convertHex(c1) << 4) + convertHex(c2)); +} + +void QScript::Lexer::record8(ushort c) +{ + Q_ASSERT(c <= 0xff); + + // enlarge buffer if full + if (pos8 >= size8 - 1) { + char *tmp = new char[2 * size8]; + memcpy(tmp, buffer8, size8 * sizeof(char)); + delete [] buffer8; + buffer8 = tmp; + size8 *= 2; + } + + buffer8[pos8++] = (char) c; +} + +void QScript::Lexer::record16(QChar c) +{ + // enlarge buffer if full + if (pos16 >= size16 - 1) { + QChar *tmp = new QChar[2 * size16]; + memcpy(tmp, buffer16, size16 * sizeof(QChar)); + delete [] buffer16; + buffer16 = tmp; + size16 *= 2; + } + + buffer16[pos16++] = c; +} + +void QScript::Lexer::recordStartPos() +{ + startlineno = yylineno; + startcolumn = yycolumn; +} + +bool QScript::Lexer::scanRegExp(RegExpBodyPrefix prefix) +{ + pos16 = 0; + bool lastWasEscape = false; + + if (prefix == EqualPrefix) + record16(QLatin1Char('=')); + + while (1) { + if (isLineTerminator() || current == 0) { + errmsg = QLatin1String("Unterminated regular expression literal"); + return false; + } + else if (current != '/' || lastWasEscape == true) + { + record16(current); + lastWasEscape = !lastWasEscape && (current == '\\'); + } + else { + pattern = QString(buffer16, pos16); + pos16 = 0; + shift(1); + break; + } + shift(1); + } + + flags = 0; + while (isIdentLetter(current)) { + record16(current); + shift(1); + } + + return true; +} + +void QScript::Lexer::syncProhibitAutomaticSemicolon() +{ + if (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; + } else { + prohibitAutomaticSemicolon = false; + } +} + + +class Translator; + +class QScriptParser: protected $table +{ +public: + QVariant val; + + struct Location { + int startLine; + int startColumn; + int endLine; + int endColumn; + }; + +public: + QScriptParser(); + ~QScriptParser(); + + bool parse(QScript::Lexer *lexer, + const QString &fileName, + Translator *translator); + + inline QString errorMessage() const + { return error_message; } + inline int errorLineNumber() const + { return error_lineno; } + inline int errorColumnNumber() const + { return error_column; } + +protected: + inline void reallocateStack(); + + inline QVariant &sym(int index) + { return sym_stack [tos + index - 1]; } + + inline Location &loc(int index) + { return location_stack [tos + index - 2]; } + +protected: + int tos; + int stack_size; + QVector<QVariant> sym_stack; + int *state_stack; + Location *location_stack; + QString error_message; + int error_lineno; + int error_column; +}; + +inline void QScriptParser::reallocateStack() +{ + if (! stack_size) + stack_size = 128; + else + stack_size <<= 1; + + sym_stack.resize(stack_size); + state_stack = reinterpret_cast<int*> (qRealloc(state_stack, stack_size * sizeof(int))); + location_stack = reinterpret_cast<Location*> (qRealloc(location_stack, stack_size * sizeof(Location))); +} + +inline static bool automatic(QScript::Lexer *lexer, int token) +{ + return (token == $table::T_RBRACE) + || (token == 0) + || lexer->prevTerminator(); +} + +QScriptParser::QScriptParser(): + tos(0), + stack_size(0), + sym_stack(0), + state_stack(0), + location_stack(0) +{ +} + +QScriptParser::~QScriptParser() +{ + if (stack_size) { + qFree(state_stack); + qFree(location_stack); + } +} + +static inline QScriptParser::Location location(QScript::Lexer *lexer) +{ + QScriptParser::Location loc; + loc.startLine = lexer->startLineNo(); + loc.startColumn = lexer->startColumnNo(); + loc.endLine = lexer->endLineNo(); + loc.endColumn = lexer->endColumnNo(); + return loc; +} + +bool QScriptParser::parse(QScript::Lexer *lexer, + const QString &fileName, + Translator *translator) +{ + const int INITIAL_STATE = 0; + + int yytoken = -1; + int saved_yytoken = -1; + int identLineNo = -1; + + reallocateStack(); + + tos = 0; + state_stack[++tos] = INITIAL_STATE; + + while (true) + { + const int state = state_stack [tos]; + if (yytoken == -1 && - TERMINAL_COUNT != action_index [state]) + { + if (saved_yytoken == -1) + { + yytoken = lexer->lex(); + location_stack [tos] = location(lexer); + } + else + { + yytoken = saved_yytoken; + saved_yytoken = -1; + } + } + + int act = t_action (state, yytoken); + + if (act == ACCEPT_STATE) + return true; + + else if (act > 0) + { + if (++tos == stack_size) + reallocateStack(); + + sym_stack [tos] = lexer->val (); + state_stack [tos] = act; + location_stack [tos] = location(lexer); + yytoken = -1; + } + + else if (act < 0) + { + int r = - act - 1; + + tos -= rhs [r]; + act = state_stack [tos++]; + + switch (r) { +./ + +PrimaryExpression: T_THIS ; + +PrimaryExpression: T_IDENTIFIER ; +/. +case $rule_number: { + sym(1) = sym(1).toByteArray(); + identLineNo = lexer->startLineNo(); +} break; +./ + +PrimaryExpression: T_NULL ; +PrimaryExpression: T_TRUE ; +PrimaryExpression: T_FALSE ; +PrimaryExpression: T_NUMERIC_LITERAL ; +PrimaryExpression: T_STRING_LITERAL ; + +PrimaryExpression: T_DIVIDE_ ; +/: +#define Q_SCRIPT_REGEXPLITERAL_RULE1 $rule_number +:/ +/. +case $rule_number: { + bool rx = lexer->scanRegExp(QScript::Lexer::NoPrefix); + if (!rx) { + error_message = lexer->errorMessage(); + error_lineno = lexer->startLineNo(); + error_column = lexer->startColumnNo(); + return false; + } +} break; +./ + +PrimaryExpression: T_DIVIDE_EQ ; +/: +#define Q_SCRIPT_REGEXPLITERAL_RULE2 $rule_number +:/ +/. +case $rule_number: { + bool rx = lexer->scanRegExp(QScript::Lexer::EqualPrefix); + if (!rx) { + error_message = lexer->errorMessage(); + error_lineno = lexer->startLineNo(); + error_column = lexer->startColumnNo(); + return false; + } +} break; +./ + +PrimaryExpression: T_LBRACKET ElisionOpt T_RBRACKET ; +PrimaryExpression: T_LBRACKET ElementList T_RBRACKET ; +PrimaryExpression: T_LBRACKET ElementList T_COMMA ElisionOpt T_RBRACKET ; +PrimaryExpression: T_LBRACE PropertyNameAndValueListOpt T_RBRACE ; +PrimaryExpression: T_LPAREN Expression T_RPAREN ; +ElementList: ElisionOpt AssignmentExpression ; +ElementList: ElementList T_COMMA ElisionOpt AssignmentExpression ; +Elision: T_COMMA ; +Elision: Elision T_COMMA ; +ElisionOpt: ; +ElisionOpt: Elision ; +PropertyNameAndValueList: PropertyName T_COLON AssignmentExpression ; +PropertyNameAndValueList: PropertyNameAndValueList T_COMMA PropertyName T_COLON AssignmentExpression ; +PropertyName: T_IDENTIFIER ; +PropertyName: T_STRING_LITERAL ; +PropertyName: T_NUMERIC_LITERAL ; +PropertyName: ReservedIdentifier ; +ReservedIdentifier: T_BREAK ; +ReservedIdentifier: T_CASE ; +ReservedIdentifier: T_CATCH ; +ReservedIdentifier: T_CONST ; +ReservedIdentifier: T_CONTINUE ; +ReservedIdentifier: T_DEBUGGER ; +ReservedIdentifier: T_DEFAULT ; +ReservedIdentifier: T_DELETE ; +ReservedIdentifier: T_DO ; +ReservedIdentifier: T_ELSE ; +ReservedIdentifier: T_FALSE ; +ReservedIdentifier: T_FINALLY ; +ReservedIdentifier: T_FOR ; +ReservedIdentifier: T_FUNCTION ; +ReservedIdentifier: T_IF ; +ReservedIdentifier: T_IN ; +ReservedIdentifier: T_INSTANCEOF ; +ReservedIdentifier: T_NEW ; +ReservedIdentifier: T_NULL ; +ReservedIdentifier: T_RESERVED_WORD ; +ReservedIdentifier: T_RETURN ; +ReservedIdentifier: T_SWITCH ; +ReservedIdentifier: T_THIS ; +ReservedIdentifier: T_THROW ; +ReservedIdentifier: T_TRUE ; +ReservedIdentifier: T_TRY ; +ReservedIdentifier: T_TYPEOF ; +ReservedIdentifier: T_VAR ; +ReservedIdentifier: T_VOID ; +ReservedIdentifier: T_WHILE ; +ReservedIdentifier: T_WITH ; +PropertyIdentifier: T_IDENTIFIER ; +PropertyIdentifier: ReservedIdentifier ; + +MemberExpression: PrimaryExpression ; +MemberExpression: FunctionExpression ; +MemberExpression: MemberExpression T_LBRACKET Expression T_RBRACKET ; +MemberExpression: MemberExpression T_DOT PropertyIdentifier ; +MemberExpression: T_NEW MemberExpression Arguments ; +NewExpression: MemberExpression ; +NewExpression: T_NEW NewExpression ; + +CallExpression: MemberExpression Arguments ; +/. +case $rule_number: { + QString name = sym(1).toString(); + if ((name == QLatin1String("qsTranslate")) || (name == QLatin1String("QT_TRANSLATE_NOOP"))) { + QVariantList args = sym(2).toList(); + if (args.size() < 2) { + qWarning("%s:%d: %s() requires at least two arguments", + qPrintable(fileName), identLineNo, qPrintable(name)); + } else { + if ((args.at(0).type() != QVariant::String) + || (args.at(1).type() != QVariant::String)) { + qWarning("%s:%d: %s(): both arguments must be literal strings", + qPrintable(fileName), identLineNo, qPrintable(name)); + } else { + QString context = args.at(0).toString(); + QString text = args.at(1).toString(); + QString comment = args.value(2).toString(); + QString extracomment; + bool plural = (args.size() > 4); + recordMessage(translator, context, text, comment, extracomment, + plural, fileName, identLineNo); + } + } + } else if ((name == QLatin1String("qsTr")) || (name == QLatin1String("QT_TR_NOOP"))) { + QVariantList args = sym(2).toList(); + if (args.size() < 1) { + qWarning("%s:%d: %s() requires at least one argument", + qPrintable(fileName), identLineNo, qPrintable(name)); + } else { + if (args.at(0).type() != QVariant::String) { + qWarning("%s:%d: %s(): text to translate must be a literal string", + qPrintable(fileName), identLineNo, qPrintable(name)); + } else { + QString context = QFileInfo(fileName).baseName(); + QString text = args.at(0).toString(); + QString comment = args.value(1).toString(); + QString extracomment; + bool plural = (args.size() > 2); + recordMessage(translator, context, text, comment, extracomment, + plural, fileName, identLineNo); + } + } + } +} break; +./ + +CallExpression: CallExpression Arguments ; +CallExpression: CallExpression T_LBRACKET Expression T_RBRACKET ; +CallExpression: CallExpression T_DOT PropertyIdentifier ; + +Arguments: T_LPAREN T_RPAREN ; +/. +case $rule_number: { + sym(1) = QVariantList(); +} break; +./ + +Arguments: T_LPAREN ArgumentList T_RPAREN ; +/. +case $rule_number: { + sym(1) = sym(2); +} break; +./ + +ArgumentList: AssignmentExpression ; +/. +case $rule_number: { + sym(1) = QVariantList() << sym(1); +} break; +./ + +ArgumentList: ArgumentList T_COMMA AssignmentExpression ; +/. +case $rule_number: { + sym(1) = sym(1).toList() << sym(3); +} break; +./ + +LeftHandSideExpression: NewExpression ; +LeftHandSideExpression: CallExpression ; +PostfixExpression: LeftHandSideExpression ; +PostfixExpression: LeftHandSideExpression T_PLUS_PLUS ; +PostfixExpression: LeftHandSideExpression T_MINUS_MINUS ; +UnaryExpression: PostfixExpression ; +UnaryExpression: T_DELETE UnaryExpression ; +UnaryExpression: T_VOID UnaryExpression ; +UnaryExpression: T_TYPEOF UnaryExpression ; +UnaryExpression: T_PLUS_PLUS UnaryExpression ; +UnaryExpression: T_MINUS_MINUS UnaryExpression ; +UnaryExpression: T_PLUS UnaryExpression ; +UnaryExpression: T_MINUS UnaryExpression ; +UnaryExpression: T_TILDE UnaryExpression ; +UnaryExpression: T_NOT UnaryExpression ; +MultiplicativeExpression: UnaryExpression ; +MultiplicativeExpression: MultiplicativeExpression T_STAR UnaryExpression ; +MultiplicativeExpression: MultiplicativeExpression T_DIVIDE_ UnaryExpression ; +MultiplicativeExpression: MultiplicativeExpression T_REMAINDER UnaryExpression ; +AdditiveExpression: MultiplicativeExpression ; + +AdditiveExpression: AdditiveExpression T_PLUS MultiplicativeExpression ; +/. +case $rule_number: { + if ((sym(1).type() == QVariant::String) || (sym(3).type() == QVariant::String)) + sym(1) = sym(1).toString() + sym(3).toString(); + else + sym(1) = QVariant(); +} break; +./ + +AdditiveExpression: AdditiveExpression T_MINUS MultiplicativeExpression ; +ShiftExpression: AdditiveExpression ; +ShiftExpression: ShiftExpression T_LT_LT AdditiveExpression ; +ShiftExpression: ShiftExpression T_GT_GT AdditiveExpression ; +ShiftExpression: ShiftExpression T_GT_GT_GT AdditiveExpression ; +RelationalExpression: ShiftExpression ; +RelationalExpression: RelationalExpression T_LT ShiftExpression ; +RelationalExpression: RelationalExpression T_GT ShiftExpression ; +RelationalExpression: RelationalExpression T_LE ShiftExpression ; +RelationalExpression: RelationalExpression T_GE ShiftExpression ; +RelationalExpression: RelationalExpression T_INSTANCEOF ShiftExpression ; +RelationalExpression: RelationalExpression T_IN ShiftExpression ; +RelationalExpressionNotIn: ShiftExpression ; +RelationalExpressionNotIn: RelationalExpressionNotIn T_LT ShiftExpression ; +RelationalExpressionNotIn: RelationalExpressionNotIn T_GT ShiftExpression ; +RelationalExpressionNotIn: RelationalExpressionNotIn T_LE ShiftExpression ; +RelationalExpressionNotIn: RelationalExpressionNotIn T_GE ShiftExpression ; +RelationalExpressionNotIn: RelationalExpressionNotIn T_INSTANCEOF ShiftExpression ; +EqualityExpression: RelationalExpression ; +EqualityExpression: EqualityExpression T_EQ_EQ RelationalExpression ; +EqualityExpression: EqualityExpression T_NOT_EQ RelationalExpression ; +EqualityExpression: EqualityExpression T_EQ_EQ_EQ RelationalExpression ; +EqualityExpression: EqualityExpression T_NOT_EQ_EQ RelationalExpression ; +EqualityExpressionNotIn: RelationalExpressionNotIn ; +EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ RelationalExpressionNotIn ; +EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ RelationalExpressionNotIn; +EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ_EQ RelationalExpressionNotIn ; +EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ_EQ RelationalExpressionNotIn ; +BitwiseANDExpression: EqualityExpression ; +BitwiseANDExpression: BitwiseANDExpression T_AND EqualityExpression ; +BitwiseANDExpressionNotIn: EqualityExpressionNotIn ; +BitwiseANDExpressionNotIn: BitwiseANDExpressionNotIn T_AND EqualityExpressionNotIn ; +BitwiseXORExpression: BitwiseANDExpression ; +BitwiseXORExpression: BitwiseXORExpression T_XOR BitwiseANDExpression ; +BitwiseXORExpressionNotIn: BitwiseANDExpressionNotIn ; +BitwiseXORExpressionNotIn: BitwiseXORExpressionNotIn T_XOR BitwiseANDExpressionNotIn ; +BitwiseORExpression: BitwiseXORExpression ; +BitwiseORExpression: BitwiseORExpression T_OR BitwiseXORExpression ; +BitwiseORExpressionNotIn: BitwiseXORExpressionNotIn ; +BitwiseORExpressionNotIn: BitwiseORExpressionNotIn T_OR BitwiseXORExpressionNotIn ; +LogicalANDExpression: BitwiseORExpression ; +LogicalANDExpression: LogicalANDExpression T_AND_AND BitwiseORExpression ; +LogicalANDExpressionNotIn: BitwiseORExpressionNotIn ; +LogicalANDExpressionNotIn: LogicalANDExpressionNotIn T_AND_AND BitwiseORExpressionNotIn ; +LogicalORExpression: LogicalANDExpression ; +LogicalORExpression: LogicalORExpression T_OR_OR LogicalANDExpression ; +LogicalORExpressionNotIn: LogicalANDExpressionNotIn ; +LogicalORExpressionNotIn: LogicalORExpressionNotIn T_OR_OR LogicalANDExpressionNotIn ; +ConditionalExpression: LogicalORExpression ; +ConditionalExpression: LogicalORExpression T_QUESTION AssignmentExpression T_COLON AssignmentExpression ; +ConditionalExpressionNotIn: LogicalORExpressionNotIn ; +ConditionalExpressionNotIn: LogicalORExpressionNotIn T_QUESTION AssignmentExpressionNotIn T_COLON AssignmentExpressionNotIn ; +AssignmentExpression: ConditionalExpression ; +AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression ; +AssignmentExpressionNotIn: ConditionalExpressionNotIn ; +AssignmentExpressionNotIn: LeftHandSideExpression AssignmentOperator AssignmentExpressionNotIn ; +AssignmentOperator: T_EQ ; +AssignmentOperator: T_STAR_EQ ; +AssignmentOperator: T_DIVIDE_EQ ; +AssignmentOperator: T_REMAINDER_EQ ; +AssignmentOperator: T_PLUS_EQ ; +AssignmentOperator: T_MINUS_EQ ; +AssignmentOperator: T_LT_LT_EQ ; +AssignmentOperator: T_GT_GT_EQ ; +AssignmentOperator: T_GT_GT_GT_EQ ; +AssignmentOperator: T_AND_EQ ; +AssignmentOperator: T_XOR_EQ ; +AssignmentOperator: T_OR_EQ ; +Expression: AssignmentExpression ; +Expression: Expression T_COMMA AssignmentExpression ; +ExpressionOpt: ; +ExpressionOpt: Expression ; +ExpressionNotIn: AssignmentExpressionNotIn ; +ExpressionNotIn: ExpressionNotIn T_COMMA AssignmentExpressionNotIn ; +ExpressionNotInOpt: ; +ExpressionNotInOpt: ExpressionNotIn ; + +Statement: Block ; +Statement: VariableStatement ; +Statement: EmptyStatement ; +Statement: ExpressionStatement ; +Statement: IfStatement ; +Statement: IterationStatement ; +Statement: ContinueStatement ; +Statement: BreakStatement ; +Statement: ReturnStatement ; +Statement: WithStatement ; +Statement: LabelledStatement ; +Statement: SwitchStatement ; +Statement: ThrowStatement ; +Statement: TryStatement ; +Statement: DebuggerStatement ; + +Block: T_LBRACE StatementListOpt T_RBRACE ; +StatementList: Statement ; +StatementList: StatementList Statement ; +StatementListOpt: ; +StatementListOpt: StatementList ; +VariableStatement: VariableDeclarationKind VariableDeclarationList T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +VariableStatement: VariableDeclarationKind VariableDeclarationList T_SEMICOLON ; +VariableDeclarationKind: T_CONST ; +VariableDeclarationKind: T_VAR ; +VariableDeclarationList: VariableDeclaration ; +VariableDeclarationList: VariableDeclarationList T_COMMA VariableDeclaration ; +VariableDeclarationListNotIn: VariableDeclarationNotIn ; +VariableDeclarationListNotIn: VariableDeclarationListNotIn T_COMMA VariableDeclarationNotIn ; +VariableDeclaration: T_IDENTIFIER InitialiserOpt ; +VariableDeclarationNotIn: T_IDENTIFIER InitialiserNotInOpt ; +Initialiser: T_EQ AssignmentExpression ; +InitialiserOpt: ; +InitialiserOpt: Initialiser ; +InitialiserNotIn: T_EQ AssignmentExpressionNotIn ; +InitialiserNotInOpt: ; +InitialiserNotInOpt: InitialiserNotIn ; +EmptyStatement: T_SEMICOLON ; +ExpressionStatement: Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ExpressionStatement: Expression T_SEMICOLON ; +IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement T_ELSE Statement ; +IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement ; +IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_SEMICOLON ; +IterationStatement: T_WHILE T_LPAREN Expression T_RPAREN Statement ; +IterationStatement: T_FOR T_LPAREN ExpressionNotInOpt T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ; +IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationListNotIn T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ; +IterationStatement: T_FOR T_LPAREN LeftHandSideExpression T_IN Expression T_RPAREN Statement ; +IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationNotIn T_IN Expression T_RPAREN Statement ; +ContinueStatement: T_CONTINUE T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ContinueStatement: T_CONTINUE T_SEMICOLON ; +ContinueStatement: T_CONTINUE T_IDENTIFIER T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ContinueStatement: T_CONTINUE T_IDENTIFIER T_SEMICOLON ; +BreakStatement: T_BREAK T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +BreakStatement: T_BREAK T_SEMICOLON ; +BreakStatement: T_BREAK T_IDENTIFIER T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +BreakStatement: T_BREAK T_IDENTIFIER T_SEMICOLON ; +ReturnStatement: T_RETURN ExpressionOpt T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ReturnStatement: T_RETURN ExpressionOpt T_SEMICOLON ; +WithStatement: T_WITH T_LPAREN Expression T_RPAREN Statement ; +SwitchStatement: T_SWITCH T_LPAREN Expression T_RPAREN CaseBlock ; +CaseBlock: T_LBRACE CaseClausesOpt T_RBRACE ; +CaseBlock: T_LBRACE CaseClausesOpt DefaultClause CaseClausesOpt T_RBRACE ; +CaseClauses: CaseClause ; +CaseClauses: CaseClauses CaseClause ; +CaseClausesOpt: ; +CaseClausesOpt: CaseClauses ; +CaseClause: T_CASE Expression T_COLON StatementListOpt ; +DefaultClause: T_DEFAULT T_COLON StatementListOpt ; +LabelledStatement: T_IDENTIFIER T_COLON Statement ; +ThrowStatement: T_THROW Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ThrowStatement: T_THROW Expression T_SEMICOLON ; +TryStatement: T_TRY Block Catch ; +TryStatement: T_TRY Block Finally ; +TryStatement: T_TRY Block Catch Finally ; +Catch: T_CATCH T_LPAREN T_IDENTIFIER T_RPAREN Block ; +Finally: T_FINALLY Block ; +DebuggerStatement: T_DEBUGGER ; +FunctionDeclaration: T_FUNCTION T_IDENTIFIER T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +FunctionExpression: T_FUNCTION IdentifierOpt T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +FormalParameterList: T_IDENTIFIER ; +FormalParameterList: FormalParameterList T_COMMA T_IDENTIFIER ; +FormalParameterListOpt: ; +FormalParameterListOpt: FormalParameterList ; +FunctionBodyOpt: ; +FunctionBodyOpt: FunctionBody ; +FunctionBody: SourceElements ; +Program: SourceElements ; +SourceElements: SourceElement ; +SourceElements: SourceElements SourceElement ; +SourceElement: Statement ; +SourceElement: FunctionDeclaration ; +IdentifierOpt: ; +IdentifierOpt: T_IDENTIFIER ; +PropertyNameAndValueListOpt: ; +PropertyNameAndValueListOpt: PropertyNameAndValueList ; + +/. + } // switch + + state_stack [tos] = nt_action (act, lhs [r] - TERMINAL_COUNT); + + if (rhs[r] > 1) { + location_stack[tos - 1].endLine = location_stack[tos + rhs[r] - 2].endLine; + location_stack[tos - 1].endColumn = location_stack[tos + rhs[r] - 2].endColumn; + location_stack[tos] = location_stack[tos + rhs[r] - 1]; + } + } + + else + { + if (saved_yytoken == -1 && automatic (lexer, yytoken) && t_action (state, T_AUTOMATIC_SEMICOLON) > 0) + { + saved_yytoken = yytoken; + yytoken = T_SEMICOLON; + continue; + } + + else if ((state == INITIAL_STATE) && (yytoken == 0)) { + // accept empty input + yytoken = T_SEMICOLON; + continue; + } + + int ers = state; + int shifts = 0; + int reduces = 0; + int expected_tokens [3]; + for (int tk = 0; tk < TERMINAL_COUNT; ++tk) + { + int k = t_action (ers, tk); + + if (! k) + continue; + else if (k < 0) + ++reduces; + else if (spell [tk]) + { + if (shifts < 3) + expected_tokens [shifts] = tk; + ++shifts; + } + } + + error_message.clear (); + if (shifts && shifts < 3) + { + bool first = true; + + for (int s = 0; s < shifts; ++s) + { + if (first) + error_message += QLatin1String ("Expected "); + else + error_message += QLatin1String (", "); + + first = false; + error_message += QLatin1String("`"); + error_message += QLatin1String (spell [expected_tokens [s]]); + error_message += QLatin1String("'"); + } + } + + if (error_message.isEmpty()) + error_message = lexer->errorMessage(); + + error_lineno = lexer->startLineNo(); + error_column = lexer->startColumnNo(); + + return false; + } + } + + return false; +} + + +bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd) +{ + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + return false; + } + QTextStream ts(&file); + QByteArray codecName; + if (!cd.m_codecForSource.isEmpty()) + codecName = cd.m_codecForSource; + else + codecName = translator.codecName(); // Just because it should be latin1 already + ts.setCodec(QTextCodec::codecForName(codecName)); + ts.setAutoDetectUnicode(true); + + QString code = ts.readAll(); + QScript::Lexer lexer; + lexer.setCode(code, /*lineNumber=*/1); + QScriptParser parser; + if (!parser.parse(&lexer, filename, &translator)) { + qWarning("%s:%d: %s", qPrintable(filename), parser.errorLineNumber(), + qPrintable(parser.errorMessage())); + return false; + } + + // Java uses UTF-16 internally and Jambi makes UTF-8 for tr() purposes of it. + translator.setCodecName("UTF-8"); + return true; +} + +QT_END_NAMESPACE +./ diff --git a/tools/linguist/lupdate/ui.cpp b/tools/linguist/lupdate/ui.cpp new file mode 100644 index 0000000..fb78b2a --- /dev/null +++ b/tools/linguist/lupdate/ui.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Linguist of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lupdate.h" + +#include <translator.h> + +#include <QtCore/QDebug> +#include <QtCore/QFile> +#include <QtCore/QString> + +#include <QtXml/QXmlAttributes> +#include <QtXml/QXmlDefaultHandler> +#include <QtXml/QXmlLocator> +#include <QtXml/QXmlParseException> + + +QT_BEGIN_NAMESPACE + +class UiReader : public QXmlDefaultHandler +{ +public: + UiReader(Translator &translator, ConversionData &cd) + : m_translator(translator), m_cd(cd), m_lineNumber(-1), m_isTrString(false), + m_needUtf8(translator.codecName() != "UTF-8") + {} + + bool startElement(const QString &namespaceURI, const QString &localName, + const QString &qName, const QXmlAttributes &atts); + bool endElement(const QString &namespaceURI, const QString &localName, + const QString &qName); + bool characters(const QString &ch); + bool fatalError(const QXmlParseException &exception); + + void setDocumentLocator(QXmlLocator *locator) { m_locator = locator; } + +private: + void flush(); + + Translator &m_translator; + ConversionData &m_cd; + QString m_context; + QString m_source; + QString m_comment; + QString m_extracomment; + QXmlLocator *m_locator; + + QString m_accum; + int m_lineNumber; + bool m_isTrString; + bool m_needUtf8; +}; + +bool UiReader::startElement(const QString &namespaceURI, + const QString &localName, const QString &qName, const QXmlAttributes &atts) +{ + Q_UNUSED(namespaceURI); + Q_UNUSED(localName); + + if (qName == QLatin1String("item")) { // UI3 menu entries + flush(); + if (!atts.value(QLatin1String("text")).isEmpty()) { + m_source = atts.value(QLatin1String("text")); + m_isTrString = true; + if (!m_cd.m_noUiLines) + m_lineNumber = m_locator->lineNumber(); + } + } else if (qName == QLatin1String("string")) { + flush(); + if (atts.value(QLatin1String("notr")).isEmpty() || + atts.value(QLatin1String("notr")) != QLatin1String("true")) { + m_isTrString = true; + m_comment = atts.value(QLatin1String("comment")); + m_extracomment = atts.value(QLatin1String("extracomment")); + if (!m_cd.m_noUiLines) + m_lineNumber = m_locator->lineNumber(); + } else { + m_isTrString = false; + } + } + m_accum.clear(); + return true; +} + +bool UiReader::endElement(const QString &namespaceURI, + const QString &localName, const QString &qName) +{ + Q_UNUSED(namespaceURI); + Q_UNUSED(localName); + + m_accum.replace(QLatin1String("\r\n"), QLatin1String("\n")); + + if (qName == QLatin1String("class")) { // UI "header" + if (m_context.isEmpty()) + m_context = m_accum; + } else if (qName == QLatin1String("string") && m_isTrString) { + m_source = m_accum; + } else if (qName == QLatin1String("comment")) { // FIXME: what's that? + m_comment = m_accum; + flush(); + } else if (qName == QLatin1String("function")) { // UI3 embedded code + fetchtrInlinedCpp(m_accum, m_translator, m_context); + } else { + flush(); + } + return true; +} + +bool UiReader::characters(const QString &ch) +{ + m_accum += ch; + return true; +} + +bool UiReader::fatalError(const QXmlParseException &exception) +{ + QString msg; + msg.sprintf("XML error: Parse error at line %d, column %d (%s).", + exception.lineNumber(), exception.columnNumber(), + exception.message().toLatin1().data()); + m_cd.appendError(msg); + return false; +} + +void UiReader::flush() +{ + if (!m_context.isEmpty() && !m_source.isEmpty()) { + TranslatorMessage msg(m_context, m_source, + m_comment, QString(), m_cd.m_sourceFileName, + m_lineNumber, QStringList()); + msg.setExtraComment(m_extracomment); + if (m_needUtf8 && msg.needs8Bit()) + msg.setUtf8(true); + m_translator.extend(msg); + } + m_source.clear(); + m_comment.clear(); + m_extracomment.clear(); +} + +bool loadUI(Translator &translator, const QString &filename, ConversionData &cd) +{ + cd.m_sourceFileName = filename; + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + return false; + } + QXmlInputSource in(&file); + QXmlSimpleReader reader; + reader.setFeature(QLatin1String("http://xml.org/sax/features/namespaces"), false); + reader.setFeature(QLatin1String("http://xml.org/sax/features/namespace-prefixes"), true); + reader.setFeature(QLatin1String( + "http://trolltech.com/xml/features/report-whitespace-only-CharData"), false); + UiReader handler(translator, cd); + reader.setContentHandler(&handler); + reader.setErrorHandler(&handler); + bool result = reader.parse(in); + if (!result) + cd.appendError(QLatin1String("Parse error in UI file")); + reader.setContentHandler(0); + reader.setErrorHandler(0); + return result; +} + +QT_END_NAMESPACE |