tools/linguist/lupdate/qscript.g
changeset 37 758a864f9613
parent 33 3e2da88830cd
--- a/tools/linguist/lupdate/qscript.g	Fri Sep 17 08:34:18 2010 +0300
+++ b/tools/linguist/lupdate/qscript.g	Mon Oct 04 01:19:32 2010 +0300
@@ -84,6 +84,7 @@
 /.
 #include <translator.h>
 
+#include <QtCore/QCoreApplication>
 #include <QtCore/qdebug.h>
 #include <QtCore/qnumeric.h>
 #include <QtCore/qstring.h>
@@ -99,15 +100,22 @@
 
 QT_BEGIN_NAMESPACE
 
+class LU {
+    Q_DECLARE_TR_FUNCTIONS(LUpdate)
+};
+
 static void recordMessage(
     Translator *tor, const QString &context, const QString &text, const QString &comment,
-    const QString &extracomment, bool plural, const QString &fileName, int lineNo)
+    const QString &extracomment, const QString &msgid, const TranslatorMessage::ExtraData &extra,
+    bool plural, const QString &fileName, int lineNo)
 {
     TranslatorMessage msg(
         context, text, comment, QString(),
         fileName, lineNo, QStringList(),
         TranslatorMessage::Unfinished, plural);
     msg.setExtraComment(extracomment.simplified());
+    msg.setId(msgid);
+    msg.setExtras(extra);
     tor->extend(msg);
 }
 
@@ -115,15 +123,23 @@
 namespace QScript
 {
 
+class CommentProcessor
+{
+public:
+    virtual ~CommentProcessor() {}
+    virtual void processComment(const QChar *chars, int length) = 0;
+};
+
 class Lexer
 {
 public:
-    Lexer();
+    Lexer(CommentProcessor *);
     ~Lexer();
 
-    void setCode(const QString &c, int lineno);
+    void setCode(const QString &c, const QString &fileName, int lineno);
     int lex();
 
+    QString fileName() const { return yyfilename; }
     int currentLineNo() const { return yylineno; }
     int currentColumnNo() const { return yycolumn; }
 
@@ -203,6 +219,7 @@
         { err = NoError; }
 
 private:
+    QString yyfilename;
     int yylineno;
     bool done;
     char *buffer8;
@@ -256,6 +273,8 @@
 
     void syncProhibitAutomaticSemicolon();
 
+    void processComment(const QChar *, int);
+
     const QChar *code;
     uint length;
     int yycolumn;
@@ -282,6 +301,8 @@
     ParenthesesState parenthesesState;
     int parenthesesCount;
     bool prohibitAutomaticSemicolon;
+
+    CommentProcessor *commentProcessor;
 };
 
 } // namespace QScript
@@ -358,7 +379,7 @@
 
 } // namespace QScript
 
-QScript::Lexer::Lexer()
+QScript::Lexer::Lexer(QScript::CommentProcessor *proc)
     :
       yylineno(0),
       size8(128), size16(128), restrKeyword(false),
@@ -369,7 +390,8 @@
       err(NoError),
       check_reserved(true),
       parenthesesState(IgnoreParentheses),
-      prohibitAutomaticSemicolon(false)
+      prohibitAutomaticSemicolon(false),
+      commentProcessor(proc)
 {
     // allocate space for read buffers
     buffer8 = new char[size8];
@@ -384,9 +406,10 @@
     delete [] buffer16;
 }
 
-void QScript::Lexer::setCode(const QString &c, int lineno)
+void QScript::Lexer::setCode(const QString &c, const QString &fileName, int lineno)
 {
     errmsg = QString();
+    yyfilename = fileName;
     yylineno = lineno;
     yycolumn = 1;
     restrKeyword = false;
@@ -735,10 +758,12 @@
             } else if (current == '/' && next1 == '/') {
                 recordStartPos();
                 shift(1);
+                Q_ASSERT(pos16 == 0);
                 state = InSingleLineComment;
             } else if (current == '/' && next1 == '*') {
                 recordStartPos();
                 shift(1);
+                Q_ASSERT(pos16 == 0);
                 state = InMultiLineComment;
             } else if (current == 0) {
                 syncProhibitAutomaticSemicolon();
@@ -797,7 +822,7 @@
                 else {
                     setDone(Bad);
                     err = IllegalCharacter;
-                    errmsg = QLatin1String("Illegal character");
+                    errmsg = LU::tr("Illegal character");
                 }
             }
             break;
@@ -808,7 +833,7 @@
             } else if (current == 0 || isLineTerminator()) {
                 setDone(Bad);
                 err = UnclosedStringLiteral;
-                errmsg = QLatin1String("Unclosed string at end of line");
+                errmsg = LU::tr("Unclosed string at end of line");
             } else if (current == '\\') {
                 state = InEscapeSequence;
             } else {
@@ -834,7 +859,7 @@
                 } else {
                     setDone(Bad);
                     err = IllegalEscapeSequence;
-                    errmsg = QLatin1String("Illegal escape squence");
+                    errmsg = LU::tr("Illegal escape squence");
                 }
             } else if (current == 'x')
                 state = InHexEscape;
@@ -873,14 +898,17 @@
             } else {
                 setDone(Bad);
                 err = IllegalUnicodeEscapeSequence;
-                errmsg = QLatin1String("Illegal unicode escape sequence");
+                errmsg = LU::tr("Illegal unicode escape sequence");
             }
             break;
         case InSingleLineComment:
             if (isLineTerminator()) {
+                record16(current); // include newline
+                processComment(buffer16, pos16);
                 shiftWindowsLineBreak();
                 yylineno++;
                 yycolumn = 0;
+                pos16 = 0;
                 terminator = true;
                 bol = true;
                 if (restrKeyword) {
@@ -890,19 +918,25 @@
                     state = Start;
             } else if (current == 0) {
                 setDone(Eof);
+            } else {
+                record16(current);
             }
             break;
         case InMultiLineComment:
             if (current == 0) {
                 setDone(Bad);
                 err = UnclosedComment;
-                errmsg = QLatin1String("Unclosed comment at end of file");
+                errmsg = LU::tr("Unclosed comment at end of file");
             } else if (isLineTerminator()) {
                 shiftWindowsLineBreak();
                 yylineno++;
             } else if (current == '*' && next1 == '/') {
+                processComment(buffer16, pos16);
+                pos16 = 0;
                 state = Start;
                 shift(1);
+            } else {
+                record16(current);
             }
             break;
         case InIdentifier:
@@ -980,7 +1014,7 @@
             } else {
                 setDone(Bad);
                 err = IllegalExponentIndicator;
-                errmsg = QLatin1String("Illegal syntax for exponential number");
+                errmsg = LU::tr("Illegal syntax for exponential number");
             }
             break;
         case InExponent:
@@ -1006,7 +1040,7 @@
          && isIdentLetter(current)) {
         state = Bad;
         err = IllegalIdentifier;
-        errmsg = QLatin1String("Identifier cannot start with numeric literal");
+        errmsg = LU::tr("Identifier cannot start with numeric literal");
     }
 
     // terminate string
@@ -1325,7 +1359,7 @@
 
     while (1) {
         if (isLineTerminator() || current == 0) {
-            errmsg = QLatin1String("Unterminated regular expression literal");
+            errmsg = LU::tr("Unterminated regular expression literal");
             return false;
         }
         else if (current != '/' || lastWasEscape == true)
@@ -1364,10 +1398,15 @@
     }
 }
 
+void QScript::Lexer::processComment(const QChar *chars, int length)
+{
+    commentProcessor->processComment(chars, length);
+}
+
 
 class Translator;
 
-class QScriptParser: protected $table
+class QScriptParser: protected $table, public QScript::CommentProcessor
 {
 public:
     QVariant val;
@@ -1383,10 +1422,12 @@
     QScriptParser();
     ~QScriptParser();
 
-    bool parse(QScript::Lexer *lexer,
-               const QString &fileName,
-               Translator *translator);
+    void setLexer(QScript::Lexer *);
 
+    bool parse(Translator *translator);
+
+    QString fileName() const
+    { return lexer->fileName(); }
     inline QString errorMessage() const
     { return error_message; }
     inline int errorLineNumber() const
@@ -1403,6 +1444,10 @@
     inline Location &loc(int index)
     { return location_stack [tos + index - 2]; }
 
+    std::ostream &yyMsg(int line = 0);
+
+    virtual void processComment(const QChar *, int);
+
 protected:
     int tos;
     int stack_size;
@@ -1412,6 +1457,13 @@
     QString error_message;
     int error_lineno;
     int error_column;
+
+private:
+    QScript::Lexer *lexer;
+    QString extracomment;
+    QString msgid;
+    QString sourcetext;
+    TranslatorMessage::ExtraData extra;
 };
 
 inline void QScriptParser::reallocateStack()
@@ -1438,7 +1490,8 @@
     stack_size(0),
     sym_stack(0),
     state_stack(0),
-    location_stack(0)
+    location_stack(0),
+    lexer(0)
 {
 }
 
@@ -1460,10 +1513,14 @@
     return loc;
 }
 
-bool QScriptParser::parse(QScript::Lexer *lexer,
-                    const QString &fileName,
-     	            Translator *translator)
+void QScriptParser::setLexer(QScript::Lexer *lex)
 {
+    lexer = lex;
+}
+
+bool QScriptParser::parse(Translator *translator)
+{
+  Q_ASSERT(lexer != 0);
   const int INITIAL_STATE = 0;
 
   int yytoken = -1;
@@ -1630,44 +1687,70 @@
 case $rule_number: {
     QString name = sym(1).toString();
     if ((name == QLatin1String("qsTranslate")) || (name == QLatin1String("QT_TRANSLATE_NOOP"))) {
+        if (!sourcetext.isEmpty())
+            yyMsg(identLineNo) << qPrintable(LU::tr("//% cannot be used with %1(). Ignoring\n").arg(name));
         QVariantList args = sym(2).toList();
         if (args.size() < 2) {
-            std::cerr << qPrintable(fileName) << ':' << identLineNo << ": "
-                      << qPrintable(name) << "() requires at least two arguments.\n";
+            yyMsg(identLineNo) << qPrintable(LU::tr("%1() requires at least two arguments.\n").arg(name));
         } else {
             if ((args.at(0).type() != QVariant::String)
                 || (args.at(1).type() != QVariant::String)) {
-                std::cerr << qPrintable(fileName) << ':' << identLineNo << ": "
-                          << qPrintable(name) << "(): both arguments must be literal strings.\n";
+                yyMsg(identLineNo) << qPrintable(LU::tr("%1(): both arguments must be literal strings.\n").arg(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);
+                              msgid, extra, plural, fileName(), identLineNo);
             }
         }
+        sourcetext.clear();
+        extracomment.clear();
+        msgid.clear();
+        extra.clear();
     } else if ((name == QLatin1String("qsTr")) || (name == QLatin1String("QT_TR_NOOP"))) {
+        if (!sourcetext.isEmpty())
+            yyMsg(identLineNo) << qPrintable(LU::tr("//% cannot be used with %1(). Ignoring\n").arg(name));
         QVariantList args = sym(2).toList();
         if (args.size() < 1) {
-            std::cerr << qPrintable(fileName) << ':' << identLineNo << ": "
-                      << qPrintable(name) << "() requires at least one argument.\n";
+            yyMsg(identLineNo) << qPrintable(LU::tr("%1() requires at least one argument.\n").arg(name));
         } else {
             if (args.at(0).type() != QVariant::String) {
-                std::cerr << qPrintable(fileName) << ':' << identLineNo << ": "
-                          << qPrintable(name) << "(): text to translate must be a literal string.\n";
+                yyMsg(identLineNo) << qPrintable(LU::tr("%1(): text to translate must be a literal string.\n").arg(name));
             } else {
-                QString context = QFileInfo(fileName).baseName();
+                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);
+                              msgid, extra, plural, fileName(), identLineNo);
             }
         }
+        sourcetext.clear();
+        extracomment.clear();
+        msgid.clear();
+        extra.clear();
+    } else if ((name == QLatin1String("qsTrId")) || (name == QLatin1String("QT_TRID_NOOP"))) {
+        if (!msgid.isEmpty())
+            yyMsg(identLineNo) << qPrintable(LU::tr("//= cannot be used with %1(). Ignoring\n").arg(name));
+        QVariantList args = sym(2).toList();
+        if (args.size() < 1) {
+            yyMsg(identLineNo) << qPrintable(LU::tr("%1() requires at least one argument.\n").arg(name));
+        } else {
+            if (args.at(0).type() != QVariant::String) {
+                yyMsg(identLineNo) << qPrintable(LU::tr("%1(): identifier must be a literal string.\n").arg(name));
+            } else {
+                msgid = args.at(0).toString();
+                bool plural = (args.size() > 1);
+                recordMessage(translator, QString(), sourcetext, QString(), extracomment,
+                              msgid, extra, plural, fileName(), identLineNo);
+            }
+        }
+        sourcetext.clear();
+        extracomment.clear();
+        msgid.clear();
+        extra.clear();
     }
 } break;
 ./
@@ -1813,20 +1896,73 @@
 ExpressionNotInOpt: ExpressionNotIn ;
 
 Statement: Block ;
+/.
+    case $rule_number:
+./
 Statement: VariableStatement ;
+/.
+    case $rule_number:
+./
 Statement: EmptyStatement ;
+/.
+    case $rule_number:
+./
 Statement: ExpressionStatement ;
+/.
+    case $rule_number:
+./
 Statement: IfStatement ;
+/.
+    case $rule_number:
+./
 Statement: IterationStatement ;
+/.
+    case $rule_number:
+./
 Statement: ContinueStatement ;
+/.
+    case $rule_number:
+./
 Statement: BreakStatement ;
+/.
+    case $rule_number:
+./
 Statement: ReturnStatement ;
+/.
+    case $rule_number:
+./
 Statement: WithStatement ;
+/.
+    case $rule_number:
+./
 Statement: LabelledStatement ;
+/.
+    case $rule_number:
+./
 Statement: SwitchStatement ;
+/.
+    case $rule_number:
+./
 Statement: ThrowStatement ;
+/.
+    case $rule_number:
+./
 Statement: TryStatement ;
+/.
+    case $rule_number:
+./
 Statement: DebuggerStatement ;
+/.
+    case $rule_number:
+    if (!sourcetext.isEmpty() || !extracomment.isEmpty() || !msgid.isEmpty() || !extra.isEmpty()) {
+        yyMsg() << qPrintable(LU::tr("Discarding unconsumed meta data\n"));
+        sourcetext.clear();
+        extracomment.clear();
+        msgid.clear();
+        extra.clear();
+    }
+    break;
+./
 
 Block: T_LBRACE StatementListOpt T_RBRACE ;
 StatementList: Statement ;
@@ -1965,6 +2101,9 @@
                 {
                   if (first)
                     error_message += QLatin1String ("Expected ");
+                    //: Beginning of the string that contains
+                    //: comma-separated list of expected tokens
+                    error_message += LU::tr("Expected ");
                   else
                     error_message += QLatin1String (", ");
 
@@ -1988,13 +2127,69 @@
     return false;
 }
 
+std::ostream &QScriptParser::yyMsg(int line)
+{
+    return std::cerr << qPrintable(fileName()) << ':' << (line ? line : lexer->startLineNo()) << ": ";
+}
+
+void QScriptParser::processComment(const QChar *chars, int length)
+{
+    if (!length)
+        return;
+    // Try to match the logic of the C++ parser.
+    if (*chars == QLatin1Char(':') && chars[1].isSpace()) {
+        extracomment += QString(chars+2, length-2);
+    } else if (*chars == QLatin1Char('=') && chars[1].isSpace()) {
+        msgid = QString(chars+2, length-2).simplified();
+    } else if (*chars == QLatin1Char('~') && chars[1].isSpace()) {
+        QString text = QString(chars+2, length-2).trimmed();
+        int k = text.indexOf(QLatin1Char(' '));
+        if (k > -1)
+            extra.insert(text.left(k), text.mid(k + 1).trimmed());
+    } else if (*chars == QLatin1Char('%') && chars[1].isSpace()) {
+        sourcetext.reserve(sourcetext.length() + length-2);
+        ushort *ptr = (ushort *)sourcetext.data() + sourcetext.length();
+        int p = 2, c;
+        forever {
+            if (p >= length)
+                break;
+            c = chars[p++].unicode();
+            if (isspace(c))
+                continue;
+            if (c != '"') {
+                yyMsg() << qPrintable(LU::tr("Unexpected character in meta string\n"));
+                break;
+            }
+            forever {
+                if (p >= length) {
+                  whoops:
+                    yyMsg() << qPrintable(LU::tr("Unterminated meta string\n"));
+                    break;
+                }
+                c = chars[p++].unicode();
+                if (c == '"')
+                    break;
+                if (c == '\\') {
+                    if (p >= length)
+                        goto whoops;
+                    c = chars[p++].unicode();
+                    if (c == '\n')
+                        goto whoops;
+                    *ptr++ = '\\';
+                }
+                *ptr++ = c;
+            }
+        }
+        sourcetext.resize(ptr - (ushort *)sourcetext.data());
+    }
+}
+
 
 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()));
+        cd.appendError(LU::tr("Cannot open %1: %2").arg(filename, file.errorString()));
         return false;
     }
     QTextStream ts(&file);
@@ -2007,10 +2202,11 @@
     ts.setAutoDetectUnicode(true);
 
     QString code = ts.readAll();
-    QScript::Lexer lexer;
-    lexer.setCode(code, /*lineNumber=*/1);
     QScriptParser parser;
-    if (!parser.parse(&lexer, filename, &translator)) {
+    QScript::Lexer lexer(&parser);
+    lexer.setCode(code, filename, /*lineNumber=*/1);
+    parser.setLexer(&lexer);
+    if (!parser.parse(&translator)) {
         std::cerr << qPrintable(filename) << ':' << parser.errorLineNumber() << ": "
                   << qPrintable(parser.errorMessage()) << std::endl;
         return false;