mirror of https://github.com/procxx/kepka.git
Replace MetaLang with codegen_lang.
This commit is contained in:
parent
1725927aea
commit
7dd24a30b5
|
@ -1,795 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "genlang.h"
|
||||
|
||||
#include <QtCore/QtPlugin>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
//Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin)
|
||||
#endif
|
||||
|
||||
typedef unsigned int uint32;
|
||||
|
||||
QString layoutDirection;
|
||||
typedef QMap<QByteArray, QString> LangKeys;
|
||||
LangKeys keys;
|
||||
typedef QMap<QByteArray, ushort> LangTags;
|
||||
LangTags tags;
|
||||
typedef QMap<QByteArray, QVector<QByteArray> > LangKeysTags;
|
||||
LangKeysTags keysTags;
|
||||
typedef QVector<QByteArray> KeysOrder;
|
||||
KeysOrder keysOrder;
|
||||
KeysOrder tagsOrder;
|
||||
typedef QMap<QByteArray, QMap<QByteArray, QVector<QString> > > LangKeysCounted;
|
||||
LangKeysCounted keysCounted;
|
||||
|
||||
static const QChar TextCommand(0x0010);
|
||||
static const QChar TextCommandLangTag(0x0020);
|
||||
|
||||
bool skipWhitespaces(const char *&from, const char *end) {
|
||||
while (from < end && (*from == ' ' || *from == '\n' || *from == '\t' || *from == '\r')) {
|
||||
++from;
|
||||
}
|
||||
return (from < end);
|
||||
}
|
||||
|
||||
bool skipComment(const char *&from, const char *end) {
|
||||
if (from >= end) return false;
|
||||
if (*from == '/') {
|
||||
if (from + 1 >= end) return true;
|
||||
if (*(from + 1) == '*') {
|
||||
from += 2;
|
||||
while (from + 1 < end && (*from != '*' || *(from + 1) != '/')) {
|
||||
++from;
|
||||
}
|
||||
from += 2;
|
||||
return (from < end);
|
||||
} else if (*(from + 1) == '/') {
|
||||
from += 2;
|
||||
while (from < end && *from != '\n' && *from != '\r') {
|
||||
++from;
|
||||
}
|
||||
if (from < end) ++from;
|
||||
return true;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool skipJunk(const char *&from, const char *end) {
|
||||
const char *start;
|
||||
do {
|
||||
start = from;
|
||||
if (!skipWhitespaces(from, end)) return false;
|
||||
if (!skipComment(from, end)) throw Exception("Unexpected end of comment!");
|
||||
} while (start != from);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool _lngEquals(const QByteArray &key, int from, int len, const char *value, int size) {
|
||||
if (size != len || from + len > key.size()) return false;
|
||||
for (const char *v = key.constData() + from, *e = v + len; v != e; ++v, ++value) {
|
||||
if (*v != *value) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#define LNG_EQUALS_PART(key, from, len, value) _lngEquals(key, from, len, value, sizeof(value) - 1)
|
||||
#define LNG_EQUALS_TAIL(key, from, value) _lngEquals(key, from, key.size() - from, value, sizeof(value) - 1)
|
||||
#define LNG_EQUALS(key, value) _lngEquals(key, 0, key.size(), value, sizeof(value) - 1)
|
||||
|
||||
static const int MaxCountedValues = 6;
|
||||
|
||||
void readKeyValue(const char *&from, const char *end) {
|
||||
if (!skipJunk(from, end)) return;
|
||||
|
||||
if (*from != '"') throw Exception(QString("Expected quote before key name!"));
|
||||
const char *nameStart = ++from;
|
||||
while (from < end && ((*from >= 'a' && *from <= 'z') || (*from >= 'A' && *from <= 'Z') || *from == '_' || (*from >= '0' && *from <= '9'))) {
|
||||
++from;
|
||||
}
|
||||
|
||||
if (from == nameStart) throw Exception(QString("Expected key name!"));
|
||||
QByteArray varName = QByteArray(nameStart, int(from - nameStart));
|
||||
for (const char *t = nameStart; t + 1 < from; ++t) {
|
||||
if (*t == '_') {
|
||||
if (*(t + 1) == '_') throw Exception(QString("Bad key name: %1").arg(QLatin1String(varName)));
|
||||
++t;
|
||||
}
|
||||
}
|
||||
|
||||
if (from == end || *from != '"') throw Exception(QString("Expected quote after key name in key '%1'!").arg(QLatin1String(varName)));
|
||||
++from;
|
||||
|
||||
if (!skipJunk(from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (*from != '=') throw Exception(QString("'=' expected in key '%1'!").arg(QLatin1String(varName)));
|
||||
|
||||
if (!skipJunk(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (*from != '"') throw Exception(QString("Expected string after '=' in key '%1'!").arg(QLatin1String(varName)));
|
||||
|
||||
QByteArray varValue;
|
||||
const char *start = ++from;
|
||||
QVector<QByteArray> tagsList;
|
||||
while (from < end && *from != '"') {
|
||||
if (*from == '\n') {
|
||||
throw Exception(QString("Unexpected end of string in key '%1'!").arg(QLatin1String(varName)));
|
||||
}
|
||||
if (*from == '\\') {
|
||||
if (from + 1 >= end) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (*(from + 1) == '"' || *(from + 1) == '\\' || *(from + 1) == '{') {
|
||||
if (from > start) varValue.append(start, int(from - start));
|
||||
start = ++from;
|
||||
} else if (*(from + 1) == 'n') {
|
||||
if (from > start) varValue.append(start, int(from - start));
|
||||
|
||||
varValue.append('\n');
|
||||
|
||||
start = (++from) + 1;
|
||||
}
|
||||
} else if (*from == '{') {
|
||||
if (from > start) varValue.append(start, int(from - start));
|
||||
|
||||
const char *tagStart = ++from;
|
||||
while (from < end && ((*from >= 'a' && *from <= 'z') || (*from >= 'A' && *from <= 'Z') || *from == '_' || (*from >= '0' && *from <= '9'))) {
|
||||
++from;
|
||||
}
|
||||
if (from == tagStart) throw Exception(QString("Expected tag name in key '%1'!").arg(QLatin1String(varName)));
|
||||
QByteArray tagName = QByteArray(tagStart, int(from - tagStart));
|
||||
|
||||
if (from == end || (*from != '}' && *from != ':')) throw Exception(QString("Expected '}' or ':' after tag name in key '%1'!").arg(QLatin1String(varName)));
|
||||
|
||||
LangTags::const_iterator i = tags.constFind(tagName);
|
||||
if (i == tags.cend()) {
|
||||
i = tags.insert(tagName, tagsOrder.size());
|
||||
tagsOrder.push_back(tagName);
|
||||
}
|
||||
if (0x0020 + *i > 0x007F) throw Exception(QString("Too many different tags in key '%1'").arg(QLatin1String(varName)));
|
||||
|
||||
QString tagReplacer(4, TextCommand);
|
||||
tagReplacer[1] = TextCommandLangTag;
|
||||
tagReplacer[2] = QChar(0x0020 + *i);
|
||||
varValue.append(tagReplacer.toUtf8());
|
||||
for (int j = 0, s = tagsList.size(); j < s; ++j) {
|
||||
if (tagsList.at(j) == tagName) throw Exception(QString("Tag '%1' double used in key '%2'!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
}
|
||||
tagsList.push_back(tagName);
|
||||
|
||||
if (*from == ':') {
|
||||
start = ++from;
|
||||
|
||||
QVector<QString> &counted(keysCounted[varName][tagName]);
|
||||
QByteArray subvarValue;
|
||||
bool foundtag = false;
|
||||
while (from < end && *from != '"' && *from != '}') {
|
||||
if (*from == '|') {
|
||||
if (from > start) subvarValue.append(start, int(from - start));
|
||||
counted.push_back(QString::fromUtf8(subvarValue));
|
||||
subvarValue = QByteArray();
|
||||
foundtag = false;
|
||||
start = from + 1;
|
||||
}
|
||||
if (*from == '\n') {
|
||||
throw Exception(QString("Unexpected end of string inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
}
|
||||
if (*from == '\\') {
|
||||
if (from + 1 >= end) throw Exception(QString("Unexpected end of file inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
if (*(from + 1) == '"' || *(from + 1) == '\\' || *(from + 1) == '{' || *(from + 1) == '#') {
|
||||
if (from > start) subvarValue.append(start, int(from - start));
|
||||
start = ++from;
|
||||
} else if (*(from + 1) == 'n') {
|
||||
if (from > start) subvarValue.append(start, int(from - start));
|
||||
|
||||
subvarValue.append('\n');
|
||||
|
||||
start = (++from) + 1;
|
||||
}
|
||||
} else if (*from == '{') {
|
||||
throw Exception(QString("Unexpected tag inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
} else if (*from == '#') {
|
||||
if (foundtag) throw Exception(QString("Replacement '#' double used inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
foundtag = true;
|
||||
if (from > start) subvarValue.append(start, int(from - start));
|
||||
subvarValue.append(tagReplacer.toUtf8());
|
||||
start = from + 1;
|
||||
}
|
||||
++from;
|
||||
}
|
||||
if (from >= end) throw Exception(QString("Unexpected end of file inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
if (*from == '"') throw Exception(QString("Unexpected end of string inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
|
||||
if (from > start) subvarValue.append(start, int(from - start));
|
||||
counted.push_back(QString::fromUtf8(subvarValue));
|
||||
|
||||
if (counted.size() > MaxCountedValues) {
|
||||
throw Exception(QString("Too many values inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
}
|
||||
}
|
||||
start = from + 1;
|
||||
}
|
||||
++from;
|
||||
}
|
||||
if (from >= end) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (from > start) varValue.append(start, int(from - start));
|
||||
|
||||
if (!skipJunk(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (*from != ';') throw Exception(QString("';' expected after \"value\" in key '%1'!").arg(QLatin1String(varName)));
|
||||
|
||||
skipJunk(++from, end);
|
||||
|
||||
if (varName == "direction") {
|
||||
throw Exception(QString("Unexpected value for 'direction' in key '%1'!").arg(QLatin1String(varName)));
|
||||
} else if (!LNG_EQUALS_PART(varName, 0, 4, "lng_")) {
|
||||
throw Exception(QString("Bad key '%1'!").arg(QLatin1String(varName)));
|
||||
} else if (keys.constFind(varName) != keys.cend()) {
|
||||
throw Exception(QString("Key '%1' doubled!").arg(QLatin1String(varName)));
|
||||
} else {
|
||||
keys.insert(varName, QString::fromUtf8(varValue));
|
||||
keysTags.insert(varName, tagsList);
|
||||
keysOrder.push_back(varName);
|
||||
}
|
||||
}
|
||||
|
||||
QString escapeCpp(const QByteArray &key, QString value) {
|
||||
if (value.isEmpty()) return "QString()";
|
||||
|
||||
QString res;
|
||||
res.reserve(value.size() * 10);
|
||||
bool instr = false;
|
||||
for (const QChar *ch = value.constData(), *e = value.constData() + value.size(); ch != e; ++ch) {
|
||||
if (ch->unicode() > 0x007F) {
|
||||
if (instr) {
|
||||
res.append('"');
|
||||
instr = false;
|
||||
}
|
||||
res.append(' ').append('u').append('"').append('\\').append('x').append(QString("%1").arg(ch->unicode(), 4, 16, QChar('0'))).append('"');
|
||||
} else {
|
||||
if (ch->unicode() == '\\' || ch->unicode() == '\n' || ch->unicode() == '\r' || ch->unicode() == '"') {
|
||||
if (!instr) {
|
||||
res.append(' ').append('u').append('"');
|
||||
instr = true;
|
||||
}
|
||||
res.append('\\');
|
||||
if (ch->unicode() == '\\' || ch->unicode() == '"') {
|
||||
res.append(*ch);
|
||||
} else if (ch->unicode() == '\n') {
|
||||
res.append('n');
|
||||
} else if (ch->unicode() == '\r') {
|
||||
res.append('r');
|
||||
}
|
||||
} else if (ch->unicode() < 0x0020) {
|
||||
if (*ch == TextCommand) {
|
||||
if (ch + 3 >= e || (ch + 1)->unicode() != TextCommandLangTag || (ch + 2)->unicode() > 0x007F || (ch + 2)->unicode() < 0x0020 || *(ch + 3) != TextCommand) {
|
||||
throw Exception(QString("Bad value for key '%1'").arg(QLatin1String(key)));
|
||||
} else {
|
||||
if (instr) {
|
||||
res.append('"');
|
||||
instr = false;
|
||||
}
|
||||
res.append(' ').append('u').append('"');
|
||||
res.append('\\').append('x').append(QString("%1").arg(ch->unicode(), 2, 16, QChar('0')));
|
||||
res.append('\\').append('x').append(QString("%1").arg((ch + 1)->unicode(), 2, 16, QChar('0')));
|
||||
res.append('\\').append('x').append(QString("%1").arg((ch + 2)->unicode(), 2, 16, QChar('0')));
|
||||
res.append('\\').append('x').append(QString("%1").arg((ch + 3)->unicode(), 2, 16, QChar('0')));
|
||||
res.append('"');
|
||||
ch += 3;
|
||||
}
|
||||
} else {
|
||||
throw Exception(QString("Bad value for key '%1'").arg(QLatin1String(key)));
|
||||
}
|
||||
} else {
|
||||
if (!instr) {
|
||||
res.append(' ').append('u').append('"');
|
||||
instr = true;
|
||||
}
|
||||
res.append(*ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (instr) res.append('"');
|
||||
return "qsl(" + res.mid(1) + ")";
|
||||
}
|
||||
|
||||
void writeCppKey(QTextStream &tcpp, const QByteArray &key, const QString &val) {
|
||||
tcpp << "\t\t\tset(" << key << ", " << escapeCpp(key, val) << ");\n";
|
||||
}
|
||||
|
||||
bool genLang(const QString &lang_in, const QString &lang_out) {
|
||||
QString lang_cpp = lang_out + ".cpp", lang_h = lang_out + ".h";
|
||||
QFile f(lang_in);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
cout << "Could not open lang input file '" << lang_in.toUtf8().constData() << "'!\n";
|
||||
QCoreApplication::exit(1);
|
||||
return false;
|
||||
}
|
||||
QByteArray checkCodec = f.read(3);
|
||||
if (checkCodec.size() < 3) {
|
||||
cout << "Bad lang input file '" << lang_in.toUtf8().constData() << "'!\n";
|
||||
QCoreApplication::exit(1);
|
||||
return false;
|
||||
}
|
||||
f.seek(0);
|
||||
|
||||
QByteArray data;
|
||||
int skip = 0;
|
||||
if ((checkCodec.at(0) == '\xFF' && checkCodec.at(1) == '\xFE') || (checkCodec.at(0) == '\xFE' && checkCodec.at(1) == '\xFF') || (checkCodec.at(1) == 0)) {
|
||||
QTextStream stream(&f);
|
||||
stream.setCodec("UTF-16");
|
||||
|
||||
QString string = stream.readAll();
|
||||
if (stream.status() != QTextStream::Ok) {
|
||||
cout << "Could not read valid UTF-16 file '" << lang_in.toUtf8().constData() << "'!\n";
|
||||
QCoreApplication::exit(1);
|
||||
return false;
|
||||
}
|
||||
f.close();
|
||||
|
||||
data = string.toUtf8();
|
||||
} else if (checkCodec.at(0) == 0) {
|
||||
QByteArray tmp = "\xFE\xFF" + f.readAll(); // add fake UTF-16 BOM
|
||||
f.close();
|
||||
|
||||
QTextStream stream(&tmp);
|
||||
stream.setCodec("UTF-16");
|
||||
QString string = stream.readAll();
|
||||
if (stream.status() != QTextStream::Ok) {
|
||||
cout << "Could not read valid UTF-16 file '" << lang_in.toUtf8().constData() << "'!\n";
|
||||
QCoreApplication::exit(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
data = string.toUtf8();
|
||||
} else {
|
||||
data = f.readAll();
|
||||
if (checkCodec.at(0) == '\xEF' && checkCodec.at(1) == '\xBB' && checkCodec.at(2) == '\xBF') {
|
||||
skip = 3; // skip UTF-8 BOM
|
||||
}
|
||||
}
|
||||
|
||||
const char *text = data.constData() + skip, *end = text + data.size() - skip;
|
||||
try {
|
||||
while (text < end) {
|
||||
readKeyValue(text, end);
|
||||
}
|
||||
|
||||
QByteArray cppText, hText;
|
||||
{
|
||||
QTextStream tcpp(&cppText), th(&hText);
|
||||
tcpp.setCodec("ISO 8859-1");
|
||||
th.setCodec("ISO 8859-1");
|
||||
th << "\
|
||||
/*\n\
|
||||
Created from \'/Resources/langs/lang.strings\' by \'/MetaLang\' project\n\
|
||||
\n\
|
||||
WARNING! All changes made in this file will be lost!\n\
|
||||
\n\
|
||||
This file is part of Telegram Desktop,\n\
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org\n\
|
||||
\n\
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify\n\
|
||||
it under the terms of the GNU General Public License as published by\n\
|
||||
the Free Software Foundation, either version 3 of the License, or\n\
|
||||
(at your option) any later version.\n\
|
||||
\n\
|
||||
It is distributed in the hope that it will be useful,\n\
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
|
||||
GNU General Public License for more details.\n\
|
||||
\n\
|
||||
In addition, as a special exception, the copyright holders give permission\n\
|
||||
to link the code of portions of this program with the OpenSSL library.\n\
|
||||
\n\
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE\n\
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org\n\
|
||||
*/\n";
|
||||
th << "#pragma once\n\n";
|
||||
|
||||
for (int i = 0, l = tagsOrder.size(); i < l; ++i) {
|
||||
th << "enum lngtag_" << tagsOrder[i] << " { lt_" << tagsOrder[i] << " = " << i << " };\n";
|
||||
}
|
||||
th << "static const ushort lngtags_cnt = " << tagsOrder.size() << ";\n";
|
||||
th << "static const ushort lngtags_max_counted_values = " << MaxCountedValues << ";\n";
|
||||
th << "\n";
|
||||
|
||||
th << "enum LangKey {\n";
|
||||
for (int i = 0, l = keysOrder.size(); i < l; ++i) {
|
||||
if (keysTags[keysOrder[i]].isEmpty()) {
|
||||
th << "\t" << keysOrder[i] << (i ? "" : " = 0") << ",\n";
|
||||
} else {
|
||||
th << "\t" << keysOrder[i] << "__tagged" << (i ? "" : " = 0") << ",\n";
|
||||
QMap<QByteArray, QVector<QString> > &countedTags(keysCounted[keysOrder[i]]);
|
||||
if (!countedTags.isEmpty()) {
|
||||
for (QMap<QByteArray, QVector<QString> >::const_iterator j = countedTags.cbegin(), e = countedTags.cend(); j != e; ++j) {
|
||||
const auto &counted(*j);
|
||||
for (int k = 0, s = counted.size(); k < s; ++k) {
|
||||
th << "\t" << keysOrder[i] << "__" + j.key() + QString::number(k).toUtf8() << ",\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
th << "\n\tlngkeys_cnt\n";
|
||||
th << "};\n\n";
|
||||
|
||||
th << "LangString lang(LangKey key);\n\n";
|
||||
th << "LangString langOriginal(LangKey key);\n\n";
|
||||
|
||||
for (int i = 0, l = keysOrder.size(); i < l; ++i) {
|
||||
QVector<QByteArray> &tagsList(keysTags[keysOrder[i]]);
|
||||
if (tagsList.isEmpty()) continue;
|
||||
|
||||
QMap<QByteArray, QVector<QString> > &countedTags(keysCounted[keysOrder[i]]);
|
||||
th << "inline LangString " << keysOrder[i] << "(";
|
||||
for (int j = 0, s = tagsList.size(); j < s; ++j) {
|
||||
if (countedTags[tagsList[j]].isEmpty()) {
|
||||
th << "lngtag_" << tagsList[j] << ", const QString &" << tagsList[j] << "__val";
|
||||
} else {
|
||||
th << "lngtag_" << tagsList[j] << ", float64 " << tagsList[j] << "__val";
|
||||
}
|
||||
if (j + 1 < s) th << ", ";
|
||||
}
|
||||
th << ") {\n";
|
||||
th << "\treturn lang(" << keysOrder[i] << "__tagged)";
|
||||
for (int j = 0, s = tagsList.size(); j < s; ++j) {
|
||||
if (countedTags[tagsList[j]].isEmpty()) {
|
||||
th << ".tag(lt_" << tagsList[j] << ", " << tagsList[j] << "__val)";
|
||||
} else {
|
||||
th << ".tag(lt_" << tagsList[j] << ", langCounted(" << keysOrder[i] << "__" << tagsList[j] << "0, lt_" << tagsList[j] << ", " << tagsList[j] << "__val))";
|
||||
}
|
||||
}
|
||||
th << ";\n";
|
||||
th << "}\n";
|
||||
}
|
||||
|
||||
tcpp << "\
|
||||
/*\n\
|
||||
Created from \'/Resources/langs/lang.strings\' by \'/MetaLang\' project\n\
|
||||
\n\
|
||||
WARNING! All changes made in this file will be lost!\n\
|
||||
\n\
|
||||
This file is part of Telegram Desktop,\n\
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org\n\
|
||||
\n\
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify\n\
|
||||
it under the terms of the GNU General Public License as published by\n\
|
||||
the Free Software Foundation, either version 3 of the License, or\n\
|
||||
(at your option) any later version.\n\
|
||||
\n\
|
||||
It is distributed in the hope that it will be useful,\n\
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
|
||||
GNU General Public License for more details.\n\
|
||||
\n\
|
||||
In addition, as a special exception, the copyright holders give permission\n\
|
||||
to link the code of portions of this program with the OpenSSL library.\n\
|
||||
\n\
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE\n\
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org\n\
|
||||
*/\n";
|
||||
tcpp << "#include \"lang.h\"\n\n";
|
||||
tcpp << "namespace {\n";
|
||||
|
||||
tcpp << "\tconst char *_langKeyNames[lngkeys_cnt] = {\n";
|
||||
for (int i = 0, l = keysOrder.size(); i < l; ++i) {
|
||||
if (keysTags[keysOrder[i]].isEmpty()) {
|
||||
tcpp << "\t\t\"" << keysOrder[i] << "\",\n";
|
||||
} else {
|
||||
tcpp << "\t\t\"" << keysOrder[i] << "__tagged\",\n";
|
||||
QMap<QByteArray, QVector<QString> > &countedTags(keysCounted[keysOrder[i]]);
|
||||
if (!countedTags.isEmpty()) {
|
||||
for (QMap<QByteArray, QVector<QString> >::const_iterator j = countedTags.cbegin(), e = countedTags.cend(); j != e; ++j) {
|
||||
const auto &counted(*j);
|
||||
for (int k = 0, s = counted.size(); k < s; ++k) {
|
||||
tcpp << "\t\t\"" << keysOrder[i] << "__" + j.key() + QString::number(k).toUtf8() << "\",\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tcpp << "\t};\n\n";
|
||||
|
||||
tcpp << "\tLangString _langValues[lngkeys_cnt], _langValuesOriginal[lngkeys_cnt];\n\n";
|
||||
tcpp << "\tvoid set(LangKey key, const QString &val) {\n";
|
||||
tcpp << "\t\t_langValues[key] = val;\n";
|
||||
tcpp << "\t}\n\n";
|
||||
|
||||
tcpp << "\tclass LangInit {\n";
|
||||
tcpp << "\tpublic:\n";
|
||||
tcpp << "\t\tLangInit() {\n";
|
||||
for (int i = 0, l = keysOrder.size(); i < l; ++i) {
|
||||
writeCppKey(tcpp, keysOrder[i] + (keysTags[keysOrder[i]].isEmpty() ? "" : "__tagged"), keys[keysOrder[i]]);
|
||||
|
||||
QMap<QByteArray, QVector<QString> > &countedTags(keysCounted[keysOrder[i]]);
|
||||
if (!countedTags.isEmpty()) {
|
||||
for (QMap<QByteArray, QVector<QString> >::const_iterator j = countedTags.cbegin(), e = countedTags.cend(); j != e; ++j) {
|
||||
const auto &counted(*j);
|
||||
for (int k = 0, s = counted.size(); k < s; ++k) {
|
||||
writeCppKey(tcpp, keysOrder[i] + "__" + j.key() + QString::number(k).toUtf8(), counted[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tcpp << "\t\t}\n";
|
||||
tcpp << "\t};\n\n";
|
||||
|
||||
tcpp << "\tLangInit _langInit;\n\n";
|
||||
|
||||
tcpp << "\tinline bool _lngEquals(const QByteArray &key, int from, int len, const char *value, int size) {\n";
|
||||
tcpp << "\t\tif (size != len || from + len > key.size()) return false;\n";
|
||||
tcpp << "\t\tfor (const char *v = key.constData() + from, *e = v + len; v != e; ++v, ++value) {\n";
|
||||
tcpp << "\t\t\tif (*v != *value) return false;\n";
|
||||
tcpp << "\t\t}\n";
|
||||
tcpp << "\t\treturn true;\n";
|
||||
tcpp << "\t}\n";
|
||||
|
||||
tcpp << "}\n\n";
|
||||
|
||||
tcpp << "#define LNG_EQUALS_PART(key, from, len, value) _lngEquals(key, from, len, value, sizeof(value) - 1)\n";
|
||||
tcpp << "#define LNG_EQUALS_TAIL(key, from, value) _lngEquals(key, from, key.size() - from, value, sizeof(value) - 1)\n";
|
||||
tcpp << "#define LNG_EQUALS(key, value) _lngEquals(key, 0, key.size(), value, sizeof(value) - 1)\n\n";
|
||||
|
||||
tcpp << "LangString lang(LangKey key) {\n";
|
||||
tcpp << "\treturn (key < 0 || key > lngkeys_cnt) ? QString() : _langValues[key];\n";
|
||||
tcpp << "}\n\n";
|
||||
|
||||
tcpp << "LangString langOriginal(LangKey key) {\n";
|
||||
tcpp << "\treturn (key < 0 || key > lngkeys_cnt || _langValuesOriginal[key] == qsl(\"{}\")) ? QString() : (_langValuesOriginal[key].isEmpty() ? _langValues[key] : _langValuesOriginal[key]);\n";
|
||||
tcpp << "}\n\n";
|
||||
|
||||
tcpp << "const char *langKeyName(LangKey key) {\n";
|
||||
tcpp << "\treturn (key < 0 || key > lngkeys_cnt) ? \"\" : _langKeyNames[key];\n";
|
||||
tcpp << "}\n\n";
|
||||
|
||||
tcpp << "ushort LangLoader::tagIndex(const QByteArray &tag) const {\n";
|
||||
tcpp << "\tif (tag.isEmpty()) return lngtags_cnt;\n\n";
|
||||
if (!tags.isEmpty()) {
|
||||
QString tab("\t");
|
||||
tcpp << "\tconst char *ch = tag.constData(), *e = tag.constData() + tag.size();\n";
|
||||
QByteArray current;
|
||||
int depth = current.size();
|
||||
tcpp << "\tswitch (*ch) {\n";
|
||||
for (LangTags::const_iterator i = tags.cbegin(), j = i + 1, e = tags.cend(); i != e; ++i) {
|
||||
QByteArray tag = i.key();
|
||||
while (depth > 0 && tag.mid(0, depth) != current) {
|
||||
tcpp << tab.repeated(depth + 1) << "}\n";
|
||||
current.chop(1);
|
||||
--depth;
|
||||
tcpp << tab.repeated(depth + 1) << "break;\n";
|
||||
}
|
||||
do {
|
||||
if (tag == current) break;
|
||||
|
||||
char ich = i.key().at(current.size());
|
||||
tcpp << tab.repeated(current.size() + 1) << "case '" << ich << "':\n";
|
||||
if (j == e || ich != ((j.key().size() > depth) ? j.key().at(depth) : 0)) {
|
||||
if (tag == current + ich) {
|
||||
tcpp << tab.repeated(depth + 1) << "\tif (ch + " << (depth + 1) << " == e) return lt_" << tag << ";\n";
|
||||
} else {
|
||||
tcpp << tab.repeated(depth + 1) << "\tif (LNG_EQUALS_TAIL(tag, " << (depth + 1) << ", \"" << i.key().mid(depth + 1) << "\")) return lt_" << tag << ";\n";
|
||||
}
|
||||
tcpp << tab.repeated(depth + 1) << "break;\n";
|
||||
break;
|
||||
}
|
||||
|
||||
++depth;
|
||||
current += ich;
|
||||
|
||||
bool exact = (tag == current);
|
||||
if (exact) {
|
||||
tcpp << tab.repeated(depth + 1) << "if (ch + " << depth << " == e) {\n";
|
||||
tcpp << tab.repeated(depth + 1) << "\treturn lt_" << tag << ";\n";
|
||||
tcpp << tab.repeated(depth + 1) << "}\n";
|
||||
}
|
||||
|
||||
QByteArray nexttag = j.key();
|
||||
if (exact && depth > 0 && nexttag.mid(0, depth) != current) {
|
||||
current.chop(1);
|
||||
--depth;
|
||||
tcpp << tab.repeated(depth + 1) << "break;\n";
|
||||
break;
|
||||
} else {
|
||||
tcpp << tab.repeated(depth + 1) << "if (ch + " << depth << " < e) switch (*(ch + " << depth << ")) {\n";
|
||||
}
|
||||
} while (true);
|
||||
++j;
|
||||
}
|
||||
while (QByteArray() != current) {
|
||||
tcpp << tab.repeated(depth + 1) << "}\n";
|
||||
current.chop(1);
|
||||
--depth;
|
||||
tcpp << tab.repeated(depth + 1) << "break;\n";
|
||||
}
|
||||
tcpp << "\t}\n\n";
|
||||
}
|
||||
tcpp << "\treturn lngtags_cnt;\n";
|
||||
tcpp << "}\n\n";
|
||||
|
||||
tcpp << "LangKey LangLoader::keyIndex(const QByteArray &key) const {\n";
|
||||
tcpp << "\tif (key.size() < 5 || !LNG_EQUALS_PART(key, 0, 4, \"lng_\")) return lngkeys_cnt;\n\n";
|
||||
if (!keys.isEmpty()) {
|
||||
QString tab("\t");
|
||||
tcpp << "\tconst char *ch = key.constData(), *e = key.constData() + key.size();\n";
|
||||
QByteArray current("lng_");
|
||||
int depth = current.size();
|
||||
tcpp << "\tswitch (*(ch + " << depth << ")) {\n";
|
||||
for (LangKeys::const_iterator i = keys.cbegin(), j = i + 1, e = keys.cend(); i != e; ++i) {
|
||||
QByteArray key = i.key();
|
||||
while (depth > 0 && key.mid(0, depth) != current) {
|
||||
tcpp << tab.repeated(depth - 3) << "}\n";
|
||||
current.chop(1);
|
||||
--depth;
|
||||
tcpp << tab.repeated(depth - 3) << "break;\n";
|
||||
}
|
||||
do {
|
||||
if (key == current) break;
|
||||
|
||||
char ich = i.key().at(current.size());
|
||||
tcpp << tab.repeated(current.size() - 3) << "case '" << ich << "':\n";
|
||||
if (j == e || ich != ((j.key().size() > depth) ? j.key().at(depth) : 0)) {
|
||||
if (key == current + ich) {
|
||||
tcpp << tab.repeated(depth - 3) << "\tif (ch + " << (depth + 1) << " == e) return " << key << (keysTags[key].isEmpty() ? "" : "__tagged") << ";\n";
|
||||
} else {
|
||||
tcpp << tab.repeated(depth - 3) << "\tif (LNG_EQUALS_TAIL(key, " << (depth + 1) << ", \"" << i.key().mid(depth + 1) << "\")) return " << key << (keysTags[key].isEmpty() ? "" : "__tagged") << ";\n";
|
||||
}
|
||||
tcpp << tab.repeated(depth - 3) << "break;\n";
|
||||
break;
|
||||
}
|
||||
|
||||
++depth;
|
||||
current += ich;
|
||||
|
||||
bool exact = (key == current);
|
||||
if (exact) {
|
||||
tcpp << tab.repeated(depth - 3) << "if (ch + " << depth << " == e) {\n";
|
||||
tcpp << tab.repeated(depth - 3) << "\treturn " << key << (keysTags[key].isEmpty() ? "" : "__tagged") << ";\n";
|
||||
tcpp << tab.repeated(depth - 3) << "}\n";
|
||||
}
|
||||
|
||||
QByteArray nextkey = j.key();
|
||||
if (exact && depth > 0 && nextkey.mid(0, depth) != current) {
|
||||
current.chop(1);
|
||||
--depth;
|
||||
tcpp << tab.repeated(depth - 3) << "break;\n";
|
||||
break;
|
||||
} else {
|
||||
tcpp << tab.repeated(depth - 3) << "if (ch + " << depth << " < e) switch (*(ch + " << depth << ")) {\n";
|
||||
}
|
||||
} while (true);
|
||||
++j;
|
||||
}
|
||||
while (QByteArray("lng_") != current) {
|
||||
tcpp << tab.repeated(depth - 3) << "}\n";
|
||||
current.chop(1);
|
||||
--depth;
|
||||
tcpp << tab.repeated(depth - 3) << "break;\n";
|
||||
}
|
||||
tcpp << "\t}\n\n";
|
||||
}
|
||||
tcpp << "\treturn lngkeys_cnt;\n";
|
||||
tcpp << "}\n\n";
|
||||
|
||||
tcpp << "bool LangLoader::tagReplaced(LangKey key, ushort tag) const {\n";
|
||||
if (!tags.isEmpty()) {
|
||||
tcpp << "\tswitch (key) {\n";
|
||||
for (int i = 0, l = keysOrder.size(); i < l; ++i) {
|
||||
QVector<QByteArray> &tagsList(keysTags[keysOrder[i]]);
|
||||
if (tagsList.isEmpty()) continue;
|
||||
|
||||
tcpp << "\tcase " << keysOrder[i] << "__tagged: {\n";
|
||||
tcpp << "\t\tswitch (tag) {\n";
|
||||
for (int j = 0, s = tagsList.size(); j < s; ++j) {
|
||||
tcpp << "\t\tcase lt_" << tagsList[j] << ":\n";
|
||||
}
|
||||
tcpp << "\t\t\treturn true;\n";
|
||||
tcpp << "\t\t}\n";
|
||||
tcpp << "\t} break;\n";
|
||||
}
|
||||
tcpp << "\t}\n\n";
|
||||
}
|
||||
tcpp << "\treturn false;";
|
||||
tcpp << "}\n\n";
|
||||
|
||||
tcpp << "LangKey LangLoader::subkeyIndex(LangKey key, ushort tag, ushort index) const {\n";
|
||||
tcpp << "\tif (index >= lngtags_max_counted_values) return lngkeys_cnt;\n\n";
|
||||
if (!tags.isEmpty()) {
|
||||
tcpp << "\tswitch (key) {\n";
|
||||
for (auto key : keysOrder) {
|
||||
QVector<QByteArray> &tagsList(keysTags[key]);
|
||||
if (tagsList.isEmpty()) continue;
|
||||
|
||||
QMap<QByteArray, QVector<QString> > &countedTags(keysCounted[key]);
|
||||
bool hasCounted = false;
|
||||
for (auto tag : tagsList) {
|
||||
if (!countedTags[tag].isEmpty()) {
|
||||
hasCounted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasCounted) continue;
|
||||
|
||||
tcpp << "\tcase " << key << "__tagged: {\n";
|
||||
tcpp << "\t\tswitch (tag) {\n";
|
||||
for (auto tag : tagsList) {
|
||||
if (!countedTags[tag].isEmpty()) {
|
||||
tcpp << "\t\tcase lt_" << tag << ": return LangKey(" << key << "__" << tag << "0 + index);\n";
|
||||
}
|
||||
}
|
||||
tcpp << "\t\t}\n";
|
||||
tcpp << "\t} break;\n";
|
||||
}
|
||||
tcpp << "\t}\n\n";
|
||||
}
|
||||
tcpp << "\treturn lngkeys_cnt;\n";
|
||||
tcpp << "}\n\n";
|
||||
|
||||
tcpp << "bool LangLoader::feedKeyValue(LangKey key, const QString &value) {\n";
|
||||
tcpp << "\tif (key < lngkeys_cnt) {\n";
|
||||
tcpp << "\t\t_found[key] = 1;\n";
|
||||
tcpp << "\t\tif (_langValuesOriginal[key].isEmpty()) {\n";
|
||||
tcpp << "\t\t\t_langValuesOriginal[key] = _langValues[key].isEmpty() ? qsl(\"{}\") : _langValues[key];\n";
|
||||
tcpp << "\t\t}\n";
|
||||
tcpp << "\t\t_langValues[key] = value;\n";
|
||||
tcpp << "\t\treturn true;\n";
|
||||
tcpp << "\t}\n";
|
||||
tcpp << "\treturn false;\n";
|
||||
tcpp << "}\n\n";
|
||||
}
|
||||
|
||||
QFile cpp(lang_cpp), h(lang_h);
|
||||
bool write_cpp = true, write_h = true;
|
||||
if (cpp.open(QIODevice::ReadOnly)) {
|
||||
QByteArray wasCpp = cpp.readAll();
|
||||
if (wasCpp.size() == cppText.size()) {
|
||||
if (!memcmp(wasCpp.constData(), cppText.constData(), cppText.size())) {
|
||||
write_cpp = false;
|
||||
}
|
||||
}
|
||||
cpp.close();
|
||||
}
|
||||
if (write_cpp) {
|
||||
if (!cpp.open(QIODevice::WriteOnly)) throw Exception("Could not open lang.cpp for writing!");
|
||||
if (cpp.write(cppText) != cppText.size()) throw Exception("Could not open lang.cpp for writing!");
|
||||
}
|
||||
if (h.open(QIODevice::ReadOnly)) {
|
||||
QByteArray wasH = h.readAll();
|
||||
if (wasH.size() == hText.size()) {
|
||||
if (!memcmp(wasH.constData(), hText.constData(), hText.size())) {
|
||||
write_h = false;
|
||||
}
|
||||
}
|
||||
h.close();
|
||||
}
|
||||
if (write_h) {
|
||||
if (!h.open(QIODevice::WriteOnly)) throw Exception("Could not open lang.h for writing!");
|
||||
if (h.write(hText) != hText.size()) throw Exception("Could not open lang.h for writing!");
|
||||
}
|
||||
} catch (exception &e) {
|
||||
cout << e.what() << "\n";
|
||||
QCoreApplication::exit(1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QRegularExpression>
|
||||
#include <QtGui/QImage>
|
||||
#include <QtGui/QPainter>
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::exception;
|
||||
|
||||
class Exception : public exception {
|
||||
public:
|
||||
|
||||
Exception(const QString &msg) : _msg(msg.toUtf8()) {
|
||||
}
|
||||
|
||||
virtual const char *what() const throw() {
|
||||
return _msg.constData();
|
||||
}
|
||||
virtual ~Exception() throw() {
|
||||
}
|
||||
|
||||
private:
|
||||
QByteArray _msg;
|
||||
};
|
||||
|
||||
bool genLang(const QString &lang_in, const QString &lang_out);
|
||||
|
||||
class GenLang : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GenLang(const QString &lang_in, const QString &lang_out) : QObject(0),
|
||||
_lang_in(lang_in), _lang_out(lang_out) {
|
||||
}
|
||||
|
||||
public slots :
|
||||
void run() {
|
||||
if (genLang(_lang_in, _lang_out)) {
|
||||
emit finished();
|
||||
}
|
||||
}
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
private:
|
||||
|
||||
QString _lang_in, _lang_out;
|
||||
};
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "mlmain.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
QString lang_in("lang.strings"), lang_out("lang");
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
if (string("-lang_in") == argv[i]) {
|
||||
if (++i < argc) lang_in = argv[i];
|
||||
} else if (string("-lang_out") == argv[i]) {
|
||||
if (++i < argc) lang_out = argv[i];
|
||||
}
|
||||
}
|
||||
#ifdef Q_OS_MAC
|
||||
if (QDir(QString()).absolutePath() == "/") {
|
||||
QString first = argc ? QString::fromLocal8Bit(argv[0]) : QString();
|
||||
if (!first.isEmpty()) {
|
||||
QFileInfo info(first);
|
||||
if (info.exists()) {
|
||||
QDir result(info.absolutePath() + "/../../..");
|
||||
QString basePath = result.absolutePath() + '/';
|
||||
lang_in = basePath + lang_in;
|
||||
lang_out = basePath + lang_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
QObject *taskImpl = new GenLang(lang_in, lang_out);
|
||||
|
||||
QCoreApplication a(argc, argv);
|
||||
|
||||
QObject::connect(taskImpl, SIGNAL(finished()), &a, SLOT(quit()));
|
||||
QTimer::singleShot(0, taskImpl, SLOT(run()));
|
||||
|
||||
return a.exec();
|
||||
}
|
|
@ -192,6 +192,9 @@ Type BasicTokenizedFile::readString() {
|
|||
while (!reader_.atEnd()) {
|
||||
auto ch = reader_.currentChar();
|
||||
if (ch == '"') {
|
||||
if (reader_.currentPtr() > offset) {
|
||||
value.append(offset, reader_.currentPtr() - offset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (ch == '\n') {
|
||||
|
@ -200,6 +203,9 @@ Type BasicTokenizedFile::readString() {
|
|||
return Type::Invalid;
|
||||
}
|
||||
if (ch == '\\') {
|
||||
if (reader_.currentPtr() > offset) {
|
||||
value.append(offset, reader_.currentPtr() - offset);
|
||||
}
|
||||
reader_.skipChar();
|
||||
ch = reader_.currentChar();
|
||||
if (reader_.atEnd() || ch == '\n') {
|
||||
|
@ -207,9 +213,6 @@ Type BasicTokenizedFile::readString() {
|
|||
failed_ = true;
|
||||
return Type::Invalid;
|
||||
}
|
||||
if (reader_.currentPtr() > offset + 1) {
|
||||
value.append(offset, reader_.currentPtr() - offset - 1);
|
||||
}
|
||||
offset = reader_.currentPtr() + 1;
|
||||
if (ch == 'n') {
|
||||
value.append('\n');
|
||||
|
@ -220,8 +223,6 @@ Type BasicTokenizedFile::readString() {
|
|||
} else if (ch == '\\') {
|
||||
value.append('\\');
|
||||
}
|
||||
} else {
|
||||
value.append(ch);
|
||||
}
|
||||
reader_.skipChar();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,493 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "codegen/lang/generator.h"
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QSet>
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtGui/QImage>
|
||||
#include <QtGui/QPainter>
|
||||
|
||||
namespace codegen {
|
||||
namespace lang {
|
||||
namespace {
|
||||
|
||||
char hexChar(uchar ch) {
|
||||
if (ch < 10) {
|
||||
return '0' + ch;
|
||||
} else if (ch < 16) {
|
||||
return 'a' + (ch - 10);
|
||||
}
|
||||
return '0';
|
||||
}
|
||||
|
||||
char hexSecondChar(char ch) {
|
||||
return hexChar((*reinterpret_cast<uchar*>(&ch)) & 0x0F);
|
||||
}
|
||||
|
||||
char hexFirstChar(char ch) {
|
||||
return hexChar((*reinterpret_cast<uchar*>(&ch)) >> 4);
|
||||
}
|
||||
|
||||
QString stringToEncodedString(const QString &str) {
|
||||
QString result, lineBreak = "\\\n";
|
||||
result.reserve(str.size() * 8);
|
||||
bool writingHexEscapedCharacters = false, startOnNewLine = false;
|
||||
int lastCutSize = 0;
|
||||
auto utf = str.toUtf8();
|
||||
for (auto ch : utf) {
|
||||
if (result.size() - lastCutSize > 80) {
|
||||
startOnNewLine = true;
|
||||
result.append(lineBreak);
|
||||
lastCutSize = result.size();
|
||||
}
|
||||
if (ch == '\n') {
|
||||
writingHexEscapedCharacters = false;
|
||||
result.append("\\n");
|
||||
} else if (ch == '\t') {
|
||||
writingHexEscapedCharacters = false;
|
||||
result.append("\\t");
|
||||
} else if (ch == '"' || ch == '\\') {
|
||||
writingHexEscapedCharacters = false;
|
||||
result.append('\\').append(ch);
|
||||
} else if (ch < 32 || static_cast<uchar>(ch) > 127) {
|
||||
writingHexEscapedCharacters = true;
|
||||
result.append("\\x").append(hexFirstChar(ch)).append(hexSecondChar(ch));
|
||||
} else {
|
||||
if (writingHexEscapedCharacters) {
|
||||
writingHexEscapedCharacters = false;
|
||||
result.append("\"\"");
|
||||
}
|
||||
result.append(ch);
|
||||
}
|
||||
}
|
||||
return '"' + (startOnNewLine ? lineBreak : QString()) + result + '"';
|
||||
}
|
||||
|
||||
QString stringToEncodedString(const std::string &str) {
|
||||
return stringToEncodedString(QString::fromStdString(str));
|
||||
}
|
||||
|
||||
QString stringToBinaryArray(const std::string &str) {
|
||||
QStringList rows, chars;
|
||||
chars.reserve(13);
|
||||
rows.reserve(1 + (str.size() / 13));
|
||||
for (uchar ch : str) {
|
||||
if (chars.size() > 12) {
|
||||
rows.push_back(chars.join(", "));
|
||||
chars.clear();
|
||||
}
|
||||
chars.push_back(QString("0x") + hexFirstChar(ch) + hexSecondChar(ch));
|
||||
}
|
||||
if (!chars.isEmpty()) {
|
||||
rows.push_back(chars.join(", "));
|
||||
}
|
||||
return QString("{") + ((rows.size() > 1) ? '\n' : ' ') + rows.join(",\n") + " }";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Generator::Generator(const Langpack &langpack, const QString &destBasePath, const common::ProjectInfo &project)
|
||||
: langpack_(langpack)
|
||||
, basePath_(destBasePath)
|
||||
, baseName_(QFileInfo(basePath_).baseName())
|
||||
, project_(project) {
|
||||
}
|
||||
|
||||
bool Generator::writeHeader() {
|
||||
header_ = std::make_unique<common::CppFile>(basePath_ + ".h", project_);
|
||||
header_->stream() << "\
|
||||
class LangString : public QString {\n\
|
||||
public:\n\
|
||||
LangString() = default;\n\
|
||||
LangString(const QString &str) : QString(str) {\n\
|
||||
}\n\
|
||||
LangString &operator=(const QString &str) {\n\
|
||||
QString::operator=(str);\n\
|
||||
return *this;\n\
|
||||
}\n\
|
||||
\n\
|
||||
LangString tag(ushort tag, const QString &replacement);\n\
|
||||
\n\
|
||||
};\n\
|
||||
\n\
|
||||
LangString langCounted(ushort key0, ushort tag, float64 value);\n\
|
||||
\n";
|
||||
auto index = 0;
|
||||
for (auto &tag : langpack_.tags) {
|
||||
header_->stream() << "enum lngtag_" << tag.tag << " { lt_" << tag.tag << " = " << index++ << " };\n";
|
||||
}
|
||||
header_->stream() << "\
|
||||
\n\
|
||||
constexpr auto lngtags_cnt = 70;\n\
|
||||
constexpr auto lngtags_max_counted_values = 6;\n\
|
||||
\n\
|
||||
enum LangKey {\n";
|
||||
for (auto &entry : langpack_.entries) {
|
||||
header_->stream() << "\t" << getFullKey(entry) << ",\n";
|
||||
}
|
||||
header_->stream() << "\
|
||||
\n\
|
||||
lngkeys_cnt,\n\
|
||||
};\n\
|
||||
\n\
|
||||
LangString lang(LangKey key);\n\
|
||||
\n\
|
||||
LangString langOriginal(LangKey key);\n\
|
||||
\n";
|
||||
for (auto &entry : langpack_.entries) {
|
||||
if (!entry.tags.empty()) {
|
||||
auto &key = entry.key;
|
||||
auto params = QStringList();
|
||||
auto invokations = QStringList();
|
||||
for (auto &tagData : entry.tags) {
|
||||
auto &tag = tagData.tag;
|
||||
auto isPlural = isTagPlural(key, tag);
|
||||
params.push_back("lngtag_" + tag + ", " + (isPlural ? "float64 " : "const QString &") + tag + "__val");
|
||||
invokations.push_back("tag(lt_" + tag + ", " + (isPlural ? ("langCounted(" + key + "__" + tag + "0, lt_" + tag + ", " + tag + "__val)") : (tag + "__val")) + ")");
|
||||
}
|
||||
header_->stream() << "\
|
||||
inline LangString " << entry.key << "(" << params.join(QString(", ")) << ") {\n\
|
||||
return lang(" << entry.key << "__tagged)." << invokations.join('.') << ";\n\
|
||||
}\n\
|
||||
\n";
|
||||
}
|
||||
}
|
||||
return header_->finalize();
|
||||
}
|
||||
|
||||
bool Generator::writeSource() {
|
||||
source_ = std::make_unique<common::CppFile>(basePath_ + ".cpp", project_);
|
||||
|
||||
source_->include("lang.h").pushNamespace().stream() << "\
|
||||
const char *_langKeyNames[lngkeys_cnt] = {\n\
|
||||
\n";
|
||||
for (auto &entry : langpack_.entries) {
|
||||
source_->stream() << "\"" << entry.key << "\",\n";
|
||||
}
|
||||
source_->stream() << "\
|
||||
\n\
|
||||
};\n\
|
||||
\n\
|
||||
LangString _langValues[lngkeys_cnt], _langValuesOriginal[lngkeys_cnt];\n\
|
||||
\n\
|
||||
void set(LangKey key, const QString &val) {\n\
|
||||
_langValues[key] = val;\n\
|
||||
}\n\
|
||||
\n\
|
||||
class LangInit {\n\
|
||||
public:\n\
|
||||
LangInit() {\n";
|
||||
for (auto &entry : langpack_.entries) {
|
||||
source_->stream() << "\t\tset(" << getFullKey(entry) << ", QString::fromUtf8(" << stringToEncodedString(entry.value) << "));\n";
|
||||
}
|
||||
source_->stream() << "\
|
||||
}\n\
|
||||
\n\
|
||||
};\n\
|
||||
\n\
|
||||
LangInit _langInit;\n\
|
||||
\n";
|
||||
|
||||
source_->popNamespace().stream() << "\
|
||||
\n\
|
||||
LangString lang(LangKey key) {\n\
|
||||
return (key < 0 || key > lngkeys_cnt) ? QString() : _langValues[key];\n\
|
||||
}\n\
|
||||
\n\
|
||||
LangString langOriginal(LangKey key) {\n\
|
||||
return (key < 0 || key > lngkeys_cnt || _langValuesOriginal[key] == qsl(\"{}\")) ? QString() : (_langValuesOriginal[key].isEmpty() ? _langValues[key] : _langValuesOriginal[key]);\n\
|
||||
}\n\
|
||||
\n\
|
||||
const char *langKeyName(LangKey key) {\n\
|
||||
return (key < 0 || key > lngkeys_cnt) ? \"\" : _langKeyNames[key];\n\
|
||||
}\n\
|
||||
\n\
|
||||
ushort LangLoader::tagIndex(QLatin1String tag) const {\n\
|
||||
auto size = tag.size();\n\
|
||||
auto data = tag.data();\n";
|
||||
|
||||
auto tagsSet = std::set<QString, std::greater<QString>>();
|
||||
for (auto &tag : langpack_.tags) {
|
||||
tagsSet.insert(tag.tag);
|
||||
}
|
||||
|
||||
writeSetSearch(tagsSet, [](const QString &tag) {
|
||||
return "lt_" + tag;
|
||||
}, "lngtags_cnt");
|
||||
|
||||
source_->stream() << "\
|
||||
}\n\
|
||||
\n\
|
||||
LangKey LangLoader::keyIndex(QLatin1String key) const {\n\
|
||||
auto size = key.size();\n\
|
||||
auto data = key.data();\n";
|
||||
|
||||
auto taggedKeys = std::map<QString, QString>();
|
||||
auto keysSet = std::set<QString, std::greater<QString>>();
|
||||
for (auto &entry : langpack_.entries) {
|
||||
if (entry.key.mid(0, entry.key.size() - 1).endsWith("__count")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto full = getFullKey(entry);
|
||||
if (full != entry.key) {
|
||||
taggedKeys.emplace(entry.key, full);
|
||||
}
|
||||
keysSet.insert(entry.key);
|
||||
}
|
||||
|
||||
writeSetSearch(keysSet, [&taggedKeys](const QString &key) {
|
||||
auto it = taggedKeys.find(key);
|
||||
return (it != taggedKeys.end()) ? it->second : key;
|
||||
}, "lngkeys_cnt");
|
||||
|
||||
source_->stream() << "\
|
||||
}\n\
|
||||
\n\
|
||||
bool LangLoader::tagReplaced(LangKey key, ushort tag) const {\n\
|
||||
switch (key) {\n";
|
||||
|
||||
for (auto &entry : langpack_.entries) {
|
||||
if (entry.tags.empty()) {
|
||||
continue;
|
||||
}
|
||||
source_->stream() << "\
|
||||
case " << entry.key << "__tagged: {\n\
|
||||
switch (tag) {\n";
|
||||
for (auto &tag : entry.tags) {
|
||||
source_->stream() << "\
|
||||
case lt_" << tag.tag << ":\n";
|
||||
}
|
||||
source_->stream() << "\
|
||||
return true;\n\
|
||||
}\n\
|
||||
} break;\n";
|
||||
}
|
||||
|
||||
source_->stream() << "\
|
||||
}\
|
||||
\n\
|
||||
return false;\n\
|
||||
}\n\
|
||||
\n\
|
||||
LangKey LangLoader::subkeyIndex(LangKey key, ushort tag, ushort index) const {\n\
|
||||
if (index >= lngtags_max_counted_values) return lngkeys_cnt;\n\
|
||||
\n\
|
||||
switch (key) {\n";
|
||||
|
||||
for (auto &entry : langpack_.entries) {
|
||||
auto cases = QString();
|
||||
for (auto &tag : entry.tags) {
|
||||
if (isTagPlural(entry.key, tag.tag)) {
|
||||
cases += "\t\t\tcase lt_" + tag.tag + ": return LangKey(" + entry.key + "__" + tag.tag + "0 + index);\n";
|
||||
}
|
||||
}
|
||||
if (cases.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
source_->stream() << "\
|
||||
case " << entry.key << "__tagged: {\n\
|
||||
switch (tag) {\n\
|
||||
" << cases << "\
|
||||
}\n\
|
||||
} break;\n";
|
||||
}
|
||||
|
||||
source_->stream() << "\
|
||||
}\n\
|
||||
\n\
|
||||
return lngkeys_cnt;\n\
|
||||
}\n\
|
||||
\n\
|
||||
bool LangLoader::feedKeyValue(LangKey key, const QString &value) {\n\
|
||||
if (key < lngkeys_cnt) {\n\
|
||||
_found[key] = 1;\n\
|
||||
if (_langValuesOriginal[key].isEmpty()) {\n\
|
||||
_langValuesOriginal[key] = _langValues[key].isEmpty() ? qsl(\"{}\") : _langValues[key];\n\
|
||||
}\n\
|
||||
_langValues[key] = value;\n\
|
||||
return true;\n\
|
||||
}\n\
|
||||
return false;\n\
|
||||
}\n";
|
||||
|
||||
return source_->finalize();
|
||||
}
|
||||
|
||||
template <typename ComputeResult>
|
||||
void Generator::writeSetSearch(const std::set<QString, std::greater<QString>> &set, ComputeResult computeResult, const QString &invalidResult) {
|
||||
auto tabs = [](int size) {
|
||||
return QString(size, '\t');
|
||||
};
|
||||
|
||||
enum class UsedCheckType {
|
||||
Switch,
|
||||
If,
|
||||
UpcomingIf,
|
||||
};
|
||||
auto checkTypes = QVector<UsedCheckType>();
|
||||
auto checkLengthHistory = QVector<int>(1, 0);
|
||||
auto chars = QString();
|
||||
auto tabsUsed = 1;
|
||||
|
||||
// Returns true if at least one check was finished.
|
||||
auto finishChecksTillKey = [this, &chars, &checkTypes, &checkLengthHistory, &tabsUsed, tabs](const QString &key) {
|
||||
auto result = false;
|
||||
while (!chars.isEmpty() && key.midRef(0, chars.size()) != chars) {
|
||||
result = true;
|
||||
|
||||
auto wasType = checkTypes.back();
|
||||
chars.resize(chars.size() - 1);
|
||||
checkTypes.pop_back();
|
||||
checkLengthHistory.pop_back();
|
||||
if (wasType == UsedCheckType::Switch || wasType == UsedCheckType::If) {
|
||||
--tabsUsed;
|
||||
if (wasType == UsedCheckType::Switch) {
|
||||
source_->stream() << tabs(tabsUsed) << "break;\n";
|
||||
}
|
||||
if ((!chars.isEmpty() && key.midRef(0, chars.size()) != chars) || key == chars) {
|
||||
source_->stream() << tabs(tabsUsed) << "}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// Check if we can use "if" for a check on "charIndex" in "it" (otherwise only "switch")
|
||||
auto canUseIfForCheck = [](auto it, auto end, int charIndex) {
|
||||
auto key = *it;
|
||||
auto i = it;
|
||||
auto keyStart = key.mid(0, charIndex);
|
||||
for (++i; i != end; ++i) {
|
||||
auto nextKey = *i;
|
||||
if (nextKey.mid(0, charIndex) != keyStart) {
|
||||
return true;
|
||||
} else if (nextKey.size() > charIndex && nextKey[charIndex] != key[charIndex]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
auto countMinimalLength = [](auto it, auto end, int charIndex) {
|
||||
auto key = *it;
|
||||
auto i = it;
|
||||
auto keyStart = key.mid(0, charIndex);
|
||||
auto result = key.size();
|
||||
for (++i; i != end; ++i) {
|
||||
auto nextKey = *i;
|
||||
if (nextKey.mid(0, charIndex) != keyStart) {
|
||||
break;
|
||||
} else if (nextKey.size() > charIndex && result > nextKey.size()) {
|
||||
result = nextKey.size();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
for (auto i = set.begin(), e = set.end(); i != e; ++i) {
|
||||
// If we use just "auto" here and "name" becomes mutable,
|
||||
// the operator[] will return QCharRef instead of QChar,
|
||||
// and "auto ch = name[index]" will behave like "auto &ch =",
|
||||
// if you assign something to "ch" after that you'll change "name" (!)
|
||||
const auto name = *i;
|
||||
|
||||
auto weContinueOldSwitch = finishChecksTillKey(name);
|
||||
while (chars.size() != name.size()) {
|
||||
auto checking = chars.size();
|
||||
auto partialKey = name.mid(0, checking);
|
||||
|
||||
auto keyChar = name[checking];
|
||||
auto usedIfForCheckCount = 0;
|
||||
auto minimalLengthCheck = countMinimalLength(i, e, checking);
|
||||
for (; checking + usedIfForCheckCount != name.size(); ++usedIfForCheckCount) {
|
||||
if (!canUseIfForCheck(i, e, checking + usedIfForCheckCount)
|
||||
|| countMinimalLength(i, e, checking + usedIfForCheckCount) != minimalLengthCheck) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto usedIfForCheck = !weContinueOldSwitch && (usedIfForCheckCount > 0);
|
||||
auto checkLengthCondition = QString();
|
||||
if (weContinueOldSwitch) {
|
||||
weContinueOldSwitch = false;
|
||||
} else {
|
||||
checkLengthCondition = (minimalLengthCheck > checkLengthHistory.back()) ? ("size >= " + QString::number(minimalLengthCheck)) : QString();
|
||||
if (!usedIfForCheck) {
|
||||
source_->stream() << tabs(tabsUsed) << (checkLengthCondition.isEmpty() ? QString() : ("if (" + checkLengthCondition + ") ")) << "switch (data[" << checking << "]) {\n";
|
||||
}
|
||||
}
|
||||
if (usedIfForCheck) {
|
||||
auto conditions = QStringList();
|
||||
if (usedIfForCheckCount > 1) {
|
||||
conditions.push_back("!memcmp(data + " + QString::number(checking) + ", \"" + name.mid(checking, usedIfForCheckCount) + "\", " + QString::number(usedIfForCheckCount) + ")");
|
||||
} else {
|
||||
conditions.push_back("data[" + QString::number(checking) + "] == '" + keyChar + "'");
|
||||
}
|
||||
if (!checkLengthCondition.isEmpty()) {
|
||||
conditions.push_front(checkLengthCondition);
|
||||
}
|
||||
source_->stream() << tabs(tabsUsed) << "if (" << conditions.join(" && ") << ") {\n";
|
||||
checkTypes.push_back(UsedCheckType::If);
|
||||
for (auto i = 1; i != usedIfForCheckCount; ++i) {
|
||||
checkTypes.push_back(UsedCheckType::UpcomingIf);
|
||||
chars.push_back(keyChar);
|
||||
checkLengthHistory.push_back(qMax(minimalLengthCheck, checkLengthHistory.back()));
|
||||
keyChar = name[checking + i];
|
||||
}
|
||||
} else {
|
||||
source_->stream() << tabs(tabsUsed) << "case '" << keyChar << "':\n";
|
||||
checkTypes.push_back(UsedCheckType::Switch);
|
||||
}
|
||||
++tabsUsed;
|
||||
chars.push_back(keyChar);
|
||||
checkLengthHistory.push_back(qMax(minimalLengthCheck, checkLengthHistory.back()));
|
||||
}
|
||||
source_->stream() << tabs(tabsUsed) << "return (size == " << chars.size() << ") ? " << computeResult(name) << " : " << invalidResult << ";\n";
|
||||
}
|
||||
finishChecksTillKey(QString());
|
||||
|
||||
source_->stream() << "\
|
||||
\n\
|
||||
return " << invalidResult << ";\n";
|
||||
}
|
||||
|
||||
QString Generator::getFullKey(const Langpack::Entry &entry) {
|
||||
if (entry.tags.empty()) {
|
||||
return entry.key;
|
||||
}
|
||||
return entry.key + "__tagged";
|
||||
}
|
||||
|
||||
bool Generator::isTagPlural(const QString &key, const QString &tag) const {
|
||||
auto searchForKey = key + "__" + tag + "0";
|
||||
for (auto &entry : langpack_.entries) {
|
||||
if (entry.key == searchForKey) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace lang
|
||||
} // namespace codegen
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <functional>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QSet>
|
||||
#include "codegen/common/cpp_file.h"
|
||||
#include "codegen/lang/parsed_file.h"
|
||||
|
||||
namespace codegen {
|
||||
namespace lang {
|
||||
|
||||
class Generator {
|
||||
public:
|
||||
Generator(const Langpack &langpack, const QString &destBasePath, const common::ProjectInfo &project);
|
||||
Generator(const Generator &other) = delete;
|
||||
Generator &operator=(const Generator &other) = delete;
|
||||
|
||||
bool writeHeader();
|
||||
bool writeSource();
|
||||
|
||||
private:
|
||||
QString getFullKey(const Langpack::Entry &entry);
|
||||
bool isTagPlural(const QString &key, const QString &tag) const;
|
||||
|
||||
template <typename ComputeResult>
|
||||
void writeSetSearch(const std::set<QString, std::greater<QString>> &set, ComputeResult computeResult, const QString &invalidResult);
|
||||
|
||||
const Langpack &langpack_;
|
||||
QString basePath_, baseName_;
|
||||
const common::ProjectInfo &project_;
|
||||
std::unique_ptr<common::CppFile> source_, header_;
|
||||
|
||||
};
|
||||
|
||||
} // namespace lang
|
||||
} // namespace codegen
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include "codegen/lang/options.h"
|
||||
#include "codegen/lang/processor.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
auto options = codegen::lang::parseOptions();
|
||||
if (options.inputPath.isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
codegen::lang::Processor processor(options);
|
||||
return processor.launch();
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "codegen/lang/options.h"
|
||||
|
||||
#include <ostream>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDir>
|
||||
#include "codegen/common/logging.h"
|
||||
|
||||
namespace codegen {
|
||||
namespace lang {
|
||||
namespace {
|
||||
|
||||
constexpr int kErrorOutputPathExpected = 902;
|
||||
constexpr int kErrorInputPathExpected = 903;
|
||||
constexpr int kErrorSingleInputPathExpected = 904;
|
||||
constexpr int kErrorWorkingPathExpected = 905;
|
||||
|
||||
} // namespace
|
||||
|
||||
using common::logError;
|
||||
|
||||
Options parseOptions() {
|
||||
Options result;
|
||||
auto args = QCoreApplication::instance()->arguments();
|
||||
for (int i = 1, count = args.size(); i < count; ++i) { // skip first
|
||||
auto &arg = args.at(i);
|
||||
|
||||
// Output path
|
||||
if (arg == "-o") {
|
||||
if (++i == count) {
|
||||
logError(kErrorOutputPathExpected, "Command Line") << "output path expected after -o";
|
||||
return Options();
|
||||
} else {
|
||||
result.outputPath = args.at(i);
|
||||
}
|
||||
} else if (arg.startsWith("-o")) {
|
||||
result.outputPath = arg.mid(2);
|
||||
|
||||
// Working path
|
||||
} else if (arg == "-w") {
|
||||
if (++i == count) {
|
||||
logError(kErrorWorkingPathExpected, "Command Line") << "working path expected after -w";
|
||||
return Options();
|
||||
} else {
|
||||
common::logSetWorkingPath(args.at(i));
|
||||
}
|
||||
} else if (arg.startsWith("-w")) {
|
||||
common::logSetWorkingPath(arg.mid(2));
|
||||
|
||||
// Input path
|
||||
} else {
|
||||
if (result.inputPath.isEmpty()) {
|
||||
result.inputPath = arg;
|
||||
} else {
|
||||
logError(kErrorSingleInputPathExpected, "Command Line") << "only one input path expected";
|
||||
return Options();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result.inputPath.isEmpty()) {
|
||||
logError(kErrorInputPathExpected, "Command Line") << "input path expected";
|
||||
return Options();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace lang
|
||||
} // namespace codegen
|
|
@ -18,6 +18,21 @@ to link the code of portions of this program with the OpenSSL library.
|
|||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include <QtCore/QTimer>
|
||||
#pragma once
|
||||
|
||||
#include "genlang.h"
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
namespace codegen {
|
||||
namespace lang {
|
||||
|
||||
struct Options {
|
||||
QString outputPath = ".";
|
||||
QString inputPath;
|
||||
};
|
||||
|
||||
// Parsing failed if inputPath is empty in the result.
|
||||
Options parseOptions();
|
||||
|
||||
} // namespace lang
|
||||
} // namespace codegen
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "codegen/lang/parsed_file.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QRegularExpression>
|
||||
#include "codegen/common/basic_tokenized_file.h"
|
||||
#include "codegen/common/logging.h"
|
||||
|
||||
namespace codegen {
|
||||
namespace lang {
|
||||
namespace {
|
||||
|
||||
using BasicToken = codegen::common::BasicTokenizedFile::Token;
|
||||
using BasicType = BasicToken::Type;
|
||||
|
||||
constexpr int kErrorBadString = 806;
|
||||
|
||||
bool ValidateAnsiString(const QString &value) {
|
||||
for (auto ch : value) {
|
||||
if (ch.unicode() > 127) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidateKey(const QString &key) {
|
||||
static const auto validator = QRegularExpression("^[a-z0-9_.-]+$", QRegularExpression::CaseInsensitiveOption);
|
||||
if (!validator.match(key).hasMatch()) {
|
||||
return false;
|
||||
}
|
||||
if (key.indexOf("__") >= 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidateTag(const QString &tag) {
|
||||
static const auto validator = QRegularExpression("^[a-z0-9_]+$", QRegularExpression::CaseInsensitiveOption);
|
||||
if (!validator.match(tag).hasMatch()) {
|
||||
return false;
|
||||
}
|
||||
if (tag.indexOf("__") >= 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QString PrepareCommandString(int index) {
|
||||
static const QChar TextCommand(0x0010);
|
||||
static const QChar TextCommandLangTag(0x0020);
|
||||
auto result = QString(4, TextCommand);
|
||||
result[1] = TextCommandLangTag;
|
||||
result[2] = QChar(0x0020 + ushort(index));
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ParsedFile::ParsedFile(const Options &options)
|
||||
: filePath_(options.inputPath)
|
||||
, file_(filePath_)
|
||||
, options_(options) {
|
||||
}
|
||||
|
||||
bool ParsedFile::read() {
|
||||
if (!file_.read()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
if (auto keyToken = file_.getToken(BasicType::String)) {
|
||||
if (ValidateKey(keyToken.value)) {
|
||||
if (auto equals = file_.getToken(BasicType::Equals)) {
|
||||
if (auto valueToken = file_.getToken(BasicType::String)) {
|
||||
assertNextToken(BasicType::Semicolon);
|
||||
addEntity(keyToken.value, valueToken.value);
|
||||
continue;
|
||||
} else {
|
||||
logErrorUnexpectedToken() << "string value for '" << keyToken.value.toStdString() << "' key";
|
||||
}
|
||||
} else {
|
||||
logErrorUnexpectedToken() << "'=' for '" << keyToken.value.toStdString() << "' key";
|
||||
}
|
||||
} else {
|
||||
logErrorUnexpectedToken() << "string key name (/^[a-z0-9_.-]+$/i)";
|
||||
}
|
||||
}
|
||||
if (file_.atEnd()) {
|
||||
break;
|
||||
}
|
||||
logErrorUnexpectedToken() << "ansi string key name";
|
||||
} while (!failed());
|
||||
|
||||
return !failed();
|
||||
}
|
||||
|
||||
BasicToken ParsedFile::assertNextToken(BasicToken::Type type) {
|
||||
auto result = file_.getToken(type);
|
||||
if (!result) {
|
||||
logErrorUnexpectedToken() << type;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
common::LogStream ParsedFile::logErrorBadString() {
|
||||
return logError(kErrorBadString);
|
||||
}
|
||||
|
||||
QString ParsedFile::extractTagsData(const QString &value, Langpack *to) {
|
||||
auto tagStart = value.indexOf('{');
|
||||
if (tagStart < 0) {
|
||||
return value;
|
||||
}
|
||||
|
||||
auto tagEnd = 0;
|
||||
auto finalValue = QString();
|
||||
finalValue.reserve(value.size() * 2);
|
||||
while (tagStart >= 0) {
|
||||
if (tagStart > tagEnd) {
|
||||
finalValue.append(value.midRef(tagEnd, tagStart - tagEnd));
|
||||
}
|
||||
++tagStart;
|
||||
tagEnd = value.indexOf('}', tagStart);
|
||||
if (tagEnd < 0) {
|
||||
logErrorBadString() << "unexpected end of value, end of tag expected.";
|
||||
return value;
|
||||
}
|
||||
finalValue.append(extractTagData(value.mid(tagStart, tagEnd - tagStart), to));
|
||||
++tagEnd;
|
||||
tagStart = value.indexOf('{', tagEnd);
|
||||
}
|
||||
if (tagEnd < value.size()) {
|
||||
finalValue.append(value.midRef(tagEnd));
|
||||
}
|
||||
return finalValue;
|
||||
}
|
||||
|
||||
QString ParsedFile::extractTagData(const QString &tagText, Langpack *to) {
|
||||
auto numericPart = tagText.indexOf(':');
|
||||
auto tag = (numericPart > 0) ? tagText.mid(0, numericPart) : tagText;
|
||||
if (!ValidateTag(tag)) {
|
||||
logErrorBadString() << "bad tag characters: '" << tagText.toStdString() << "'";
|
||||
return QString();
|
||||
}
|
||||
for (auto &previousTag : to->tags) {
|
||||
if (previousTag.tag == tag) {
|
||||
logErrorBadString() << "duplicate found for tag '" << tagText.toStdString() << "'";
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
auto index = 0;
|
||||
auto tagIndex = result_.tags.size();
|
||||
for (auto &alreadyTag : result_.tags) {
|
||||
if (alreadyTag.tag == tag) {
|
||||
tagIndex = index;
|
||||
break;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
if (tagIndex == result_.tags.size()) {
|
||||
result_.tags.push_back({ tag });
|
||||
}
|
||||
if (numericPart > 0) {
|
||||
auto numericParts = tagText.mid(numericPart + 1).split('|');
|
||||
if (numericParts.size() != 3) {
|
||||
logErrorBadString() << "bad option count for plural key part in tag: '" << tagText.toStdString() << "'";
|
||||
return QString();
|
||||
}
|
||||
auto index = 0;
|
||||
for (auto &part : numericParts) {
|
||||
auto numericPartEntry = Langpack::Entry();
|
||||
numericPartEntry.key = tag + QString::number(index++);
|
||||
if (part.indexOf('#') != part.lastIndexOf('#')) {
|
||||
logErrorBadString() << "bad option for plural key part in tag: '" << tagText.toStdString() << "', too many '#'.";
|
||||
return QString();
|
||||
}
|
||||
numericPartEntry.value = part.replace('#', PrepareCommandString(tagIndex));
|
||||
to->entries.push_back(numericPartEntry);
|
||||
}
|
||||
}
|
||||
to->tags.push_back({ tag });
|
||||
return PrepareCommandString(tagIndex);
|
||||
}
|
||||
|
||||
void ParsedFile::addEntity(const QString &key, const QString &value) {
|
||||
for (auto &entry : result_.entries) {
|
||||
if (entry.key == key) {
|
||||
logError(kErrorBadString) << "duplicate found for key '" << key.toStdString() << "'";
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto tagsData = Langpack();
|
||||
auto entry = Langpack::Entry();
|
||||
entry.key = key;
|
||||
entry.value = extractTagsData(value, &tagsData);
|
||||
entry.tags = tagsData.tags;
|
||||
result_.entries.push_back(entry);
|
||||
for (auto &pluralEntry : tagsData.entries) {
|
||||
auto taggedEntry = Langpack::Entry();
|
||||
taggedEntry.key = key + "__" + pluralEntry.key;
|
||||
taggedEntry.value = pluralEntry.value;
|
||||
result_.entries.push_back(taggedEntry);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lang
|
||||
} // namespace codegen
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <QImage>
|
||||
#include "codegen/common/basic_tokenized_file.h"
|
||||
#include "codegen/lang/options.h"
|
||||
|
||||
namespace codegen {
|
||||
namespace lang {
|
||||
|
||||
struct Langpack {
|
||||
struct Tag {
|
||||
QString tag;
|
||||
};
|
||||
struct Entry {
|
||||
QString key;
|
||||
QString value;
|
||||
std::vector<Tag> tags;
|
||||
};
|
||||
std::vector<Entry> entries;
|
||||
std::vector<Tag> tags;
|
||||
|
||||
};
|
||||
|
||||
// Parses an input file to the internal struct.
|
||||
class ParsedFile {
|
||||
public:
|
||||
explicit ParsedFile(const Options &options);
|
||||
ParsedFile(const ParsedFile &other) = delete;
|
||||
ParsedFile &operator=(const ParsedFile &other) = delete;
|
||||
|
||||
bool read();
|
||||
|
||||
Langpack getResult() {
|
||||
return result_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool failed() const {
|
||||
return failed_ || file_.failed();
|
||||
}
|
||||
|
||||
// Log error to std::cerr with 'code' at the current position in file.
|
||||
common::LogStream logError(int code) {
|
||||
failed_ = true;
|
||||
return file_.logError(code);
|
||||
}
|
||||
common::LogStream logErrorUnexpectedToken() {
|
||||
failed_ = true;
|
||||
return file_.logErrorUnexpectedToken();
|
||||
}
|
||||
common::LogStream logErrorBadString();
|
||||
common::LogStream logAssert(bool assertion) {
|
||||
if (!assertion) {
|
||||
return logError(common::kErrorInternal) << "internal - ";
|
||||
}
|
||||
return common::LogStream(common::LogStream::Null);
|
||||
}
|
||||
|
||||
// Read next token and fire unexpected token error if it is not of "type".
|
||||
using BasicToken = common::BasicTokenizedFile::Token;
|
||||
BasicToken assertNextToken(BasicToken::Type type);
|
||||
|
||||
void addEntity(const QString &key, const QString &value);
|
||||
QString extractTagsData(const QString &value, Langpack *to);
|
||||
QString extractTagData(const QString &tag, Langpack *to);
|
||||
|
||||
QString filePath_;
|
||||
common::BasicTokenizedFile file_;
|
||||
Options options_;
|
||||
bool failed_ = false;
|
||||
Langpack result_;
|
||||
|
||||
};
|
||||
|
||||
} // namespace lang
|
||||
} // namespace codegen
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "codegen/lang/processor.h"
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include "codegen/common/cpp_file.h"
|
||||
#include "codegen/lang/parsed_file.h"
|
||||
#include "codegen/lang/generator.h"
|
||||
|
||||
namespace codegen {
|
||||
namespace lang {
|
||||
namespace {
|
||||
|
||||
constexpr int kErrorCantWritePath = 821;
|
||||
|
||||
} // namespace
|
||||
|
||||
Processor::Processor(const Options &options)
|
||||
: parser_(std::make_unique<ParsedFile>(options))
|
||||
, options_(options) {
|
||||
}
|
||||
|
||||
int Processor::launch() {
|
||||
if (!parser_->read()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!write(parser_->getResult())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Processor::write(const Langpack &langpack) const {
|
||||
bool forceReGenerate = false;
|
||||
QDir dir(options_.outputPath);
|
||||
if (!dir.mkpath(".")) {
|
||||
common::logError(kErrorCantWritePath, "Command Line") << "can not open path for writing: " << dir.absolutePath().toStdString();
|
||||
return false;
|
||||
}
|
||||
|
||||
QFileInfo srcFile(options_.inputPath);
|
||||
QString dstFilePath = dir.absolutePath() + "/lang_auto";
|
||||
|
||||
common::ProjectInfo project = {
|
||||
"codegen_style",
|
||||
srcFile.fileName(),
|
||||
forceReGenerate
|
||||
};
|
||||
|
||||
Generator generator(langpack, dstFilePath, project);
|
||||
if (!generator.writeHeader()) {
|
||||
return false;
|
||||
}
|
||||
if (!generator.writeSource()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Processor::~Processor() = default;
|
||||
|
||||
} // namespace lang
|
||||
} // namespace codegen
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QtCore/QString>
|
||||
#include "codegen/lang/options.h"
|
||||
|
||||
namespace codegen {
|
||||
namespace lang {
|
||||
class ParsedFile;
|
||||
struct Langpack;
|
||||
|
||||
// Walks through a file, parses it and generates the output.
|
||||
class Processor {
|
||||
public:
|
||||
explicit Processor(const Options &options);
|
||||
Processor(const Processor &other) = delete;
|
||||
Processor &operator=(const Processor &other) = delete;
|
||||
|
||||
// Returns 0 on success.
|
||||
int launch();
|
||||
|
||||
~Processor();
|
||||
|
||||
private:
|
||||
bool write(const Langpack &langpack) const;
|
||||
|
||||
std::unique_ptr<ParsedFile> parser_;
|
||||
const Options &options_;
|
||||
|
||||
};
|
||||
|
||||
} // namespace lang
|
||||
} // namespace codegen
|
|
@ -31,6 +31,7 @@ namespace {
|
|||
constexpr int kErrorOutputPathExpected = 902;
|
||||
constexpr int kErrorInputPathExpected = 903;
|
||||
constexpr int kErrorSingleInputPathExpected = 904;
|
||||
constexpr int kErrorWorkingPathExpected = 905;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -53,6 +54,17 @@ Options parseOptions() {
|
|||
} else if (arg.startsWith("-o")) {
|
||||
result.outputPath = arg.mid(2);
|
||||
|
||||
// Working path
|
||||
} else if (arg == "-w") {
|
||||
if (++i == count) {
|
||||
logError(kErrorWorkingPathExpected, "Command Line") << "working path expected after -w";
|
||||
return Options();
|
||||
} else {
|
||||
common::logSetWorkingPath(args.at(i));
|
||||
}
|
||||
} else if (arg.startsWith("-w")) {
|
||||
common::logSetWorkingPath(arg.mid(2));
|
||||
|
||||
// Input path
|
||||
} else {
|
||||
if (result.inputPath.isEmpty()) {
|
||||
|
|
|
@ -1133,11 +1133,11 @@ void initPxValues() {\n\
|
|||
if (cRetina()) return;\n\
|
||||
\n\
|
||||
switch (cScale()) {\n";
|
||||
for (int i = 1, scalesCount = scales.size(); i < scalesCount; ++i) {
|
||||
source_->stream() << "\tcase " << scaleNames.at(i) << ":\n";
|
||||
for (int i = 1, scalesCount = _scales.size(); i < scalesCount; ++i) {
|
||||
source_->stream() << "\tcase " << _scaleNames.at(i) << ":\n";
|
||||
for (auto it = pxValues_.cbegin(), e = pxValues_.cend(); it != e; ++it) {
|
||||
auto value = it.key();
|
||||
int adjusted = structure::data::pxAdjust(value, scales.at(i));
|
||||
int adjusted = structure::data::pxAdjust(value, _scales.at(i));
|
||||
if (adjusted != value) {
|
||||
source_->stream() << "\t\t" << pxValueName(value) << " = " << adjusted << ";\n";
|
||||
}
|
||||
|
|
|
@ -77,8 +77,8 @@ private:
|
|||
QMap<QString, int> iconMasks_; // icon file -> index
|
||||
std::map<QString, int, std::greater<QString>> paletteIndices_;
|
||||
|
||||
std::vector<int> scales = { 4, 5, 6, 8 }; // scale / 4 gives our 1.00, 1.25, 1.50, 2.00
|
||||
std::vector<const char *>scaleNames = { "dbisOne", "dbisOneAndQuarter", "dbisOneAndHalf", "dbisTwo" };
|
||||
std::vector<int> _scales = { 4, 5, 6, 8 }; // scale / 4 gives our 1.00, 1.25, 1.50, 2.00
|
||||
std::vector<const char *> _scaleNames = { "dbisOne", "dbisOneAndQuarter", "dbisOneAndHalf", "dbisTwo" };
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -50,10 +50,6 @@ private:
|
|||
std::unique_ptr<ParsedFile> parser_;
|
||||
const Options &options_;
|
||||
|
||||
// List of files we need to generate with other instance of Generator.
|
||||
// It is not empty only if rebuild_ flag is true.
|
||||
QStringList dependenciesToGenerate_;
|
||||
|
||||
};
|
||||
|
||||
} // namespace style
|
||||
|
|
|
@ -22,6 +22,35 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
|
||||
#include "langloaderplain.h"
|
||||
|
||||
LangString LangString::tag(ushort tag, const QString &replacement) {
|
||||
for (const QChar *s = constData(), *ch = s, *e = ch + size(); ch != e;) {
|
||||
if (*ch == TextCommand) {
|
||||
if (ch + 3 < e && (ch + 1)->unicode() == TextCommandLangTag && *(ch + 3) == TextCommand) {
|
||||
if ((ch + 2)->unicode() == 0x0020 + tag) {
|
||||
LangString result;
|
||||
result.reserve(size() + replacement.size() - 4);
|
||||
if (ch > s) result.append(midRef(0, ch - s));
|
||||
result.append(replacement);
|
||||
if (ch + 4 < e) result.append(midRef(ch - s + 4));
|
||||
return result;
|
||||
} else {
|
||||
ch += 4;
|
||||
}
|
||||
} else {
|
||||
const QChar *next = textSkipCommand(ch, e);
|
||||
if (next == ch) {
|
||||
++ch;
|
||||
} else {
|
||||
ch = next;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
++ch;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
LangString langCounted(ushort key0, ushort tag, float64 value) { // current lang dependent
|
||||
int v = qFloor(value);
|
||||
QString sv;
|
||||
|
|
|
@ -20,6 +20,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "lang_auto.h"
|
||||
|
||||
constexpr const str_const LanguageCodes[] = {
|
||||
"en",
|
||||
"it",
|
||||
|
@ -31,53 +33,6 @@ constexpr const str_const LanguageCodes[] = {
|
|||
};
|
||||
constexpr const int languageTest = -1, languageDefault = 0, languageCount = base::array_size(LanguageCodes);
|
||||
|
||||
class LangString : public QString {
|
||||
public:
|
||||
LangString() {
|
||||
}
|
||||
LangString(const QString &str) : QString(str) {
|
||||
}
|
||||
|
||||
LangString tag(ushort tag, const QString &replacement) {
|
||||
for (const QChar *s = constData(), *ch = s, *e = ch + size(); ch != e;) {
|
||||
if (*ch == TextCommand) {
|
||||
if (ch + 3 < e && (ch + 1)->unicode() == TextCommandLangTag && *(ch + 3) == TextCommand) {
|
||||
if ((ch + 2)->unicode() == 0x0020 + tag) {
|
||||
LangString result;
|
||||
result.reserve(size() + replacement.size() - 4);
|
||||
if (ch > s) result.append(midRef(0, ch - s));
|
||||
result.append(replacement);
|
||||
if (ch + 4 < e) result.append(midRef(ch - s + 4));
|
||||
return result;
|
||||
} else {
|
||||
ch += 4;
|
||||
}
|
||||
} else {
|
||||
const QChar *next = textSkipCommand(ch, e);
|
||||
if (next == ch) {
|
||||
++ch;
|
||||
} else {
|
||||
ch = next;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
++ch;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
LangString &operator=(const QString &str) {
|
||||
QString::operator=(str);
|
||||
return (*this);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LangString langCounted(ushort key0, ushort tag, float64 value);
|
||||
|
||||
#include "lang_auto.h"
|
||||
|
||||
const char *langKeyName(LangKey key);
|
||||
|
||||
template <typename WithYear, typename WithoutYear>
|
||||
|
@ -187,8 +142,8 @@ protected:
|
|||
memset(_found, 0, sizeof(_found));
|
||||
}
|
||||
|
||||
ushort tagIndex(const QByteArray &tag) const;
|
||||
LangKey keyIndex(const QByteArray &key) const;
|
||||
ushort tagIndex(QLatin1String tag) const;
|
||||
LangKey keyIndex(QLatin1String key) const;
|
||||
bool tagReplaced(LangKey key, ushort tag) const;
|
||||
LangKey subkeyIndex(LangKey key, ushort tag, ushort index) const;
|
||||
|
||||
|
|
|
@ -33,22 +33,22 @@ bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) {
|
|||
++from;
|
||||
}
|
||||
|
||||
QByteArray varName = QByteArray(nameStart, from - nameStart);
|
||||
auto varName = QLatin1String(nameStart, from - nameStart);
|
||||
|
||||
if (from == end || *from != '"') throw Exception(QString("Expected quote after key name '%1'!").arg(QLatin1String(varName)));
|
||||
if (from == end || *from != '"') throw Exception(QString("Expected quote after key name '%1'!").arg(varName));
|
||||
++from;
|
||||
|
||||
if (!skipWhitespaces(from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (*from != '=') throw Exception(QString("'=' expected in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (!skipWhitespaces(from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(varName));
|
||||
if (*from != '=') throw Exception(QString("'=' expected in key '%1'!").arg(varName));
|
||||
|
||||
if (!skipWhitespaces(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (*from != '"') throw Exception(QString("Expected string after '=' in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (!skipWhitespaces(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(varName));
|
||||
if (*from != '"') throw Exception(QString("Expected string after '=' in key '%1'!").arg(varName));
|
||||
|
||||
LangKey varKey = keyIndex(varName);
|
||||
bool feedingValue = request.isEmpty();
|
||||
if (feedingValue) {
|
||||
if (varKey == lngkeys_cnt) {
|
||||
warning(QString("Unknown key '%1'!").arg(QLatin1String(varName)));
|
||||
warning(QString("Unknown key '%1'!").arg(varName));
|
||||
}
|
||||
} else if (!readingAll && !request.contains(varKey)) {
|
||||
varKey = lngkeys_cnt;
|
||||
|
@ -60,10 +60,10 @@ bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) {
|
|||
const char *start = ++from;
|
||||
while (from < end && *from != '"') {
|
||||
if (*from == '\n') {
|
||||
throw Exception(QString("Unexpected end of string in key '%1'!").arg(QLatin1String(varName)));
|
||||
throw Exception(QString("Unexpected end of string in key '%1'!").arg(varName));
|
||||
}
|
||||
if (*from == '\\') {
|
||||
if (from + 1 >= end) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (from + 1 >= end) throw Exception(QString("Unexpected end of file in key '%1'!").arg(varName));
|
||||
if (*(from + 1) == '"' || *(from + 1) == '\\' || *(from + 1) == '{') {
|
||||
if (readingValue && from > start) varValue.append(start, from - start);
|
||||
start = ++from;
|
||||
|
@ -83,26 +83,26 @@ bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) {
|
|||
}
|
||||
if (from == tagStart) {
|
||||
readingValue = false;
|
||||
warning(QString("Expected tag name in key '%1'!").arg(QLatin1String(varName)));
|
||||
warning(QString("Expected tag name in key '%1'!").arg(varName));
|
||||
continue;
|
||||
}
|
||||
QByteArray tagName = QByteArray(tagStart, int(from - tagStart));
|
||||
auto tagName = QLatin1String(tagStart, int(from - tagStart));
|
||||
|
||||
if (from == end || (*from != '}' && *from != ':')) throw Exception(QString("Expected '}' or ':' after tag name in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (from == end || (*from != '}' && *from != ':')) throw Exception(QString("Expected '}' or ':' after tag name in key '%1'!").arg(varName));
|
||||
|
||||
ushort index = tagIndex(tagName);
|
||||
if (index == lngtags_cnt) {
|
||||
readingValue = false;
|
||||
warning(QString("Tag '%1' not found in key '%2', not using value.").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
warning(QString("Tag '%1' not found in key '%2', not using value.").arg(tagName).arg(varName));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!tagReplaced(varKey, index)) {
|
||||
readingValue = false;
|
||||
warning(QString("Unexpected tag '%1' in key '%2', not using value.").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
warning(QString("Unexpected tag '%1' in key '%2', not using value.").arg(tagName).arg(varName));
|
||||
continue;
|
||||
}
|
||||
if (tagsUsed.contains(index)) throw Exception(QString("Tag '%1' double used in key '%2'!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
if (tagsUsed.contains(index)) throw Exception(QString("Tag '%1' double used in key '%2'!").arg(tagName).arg(varName));
|
||||
tagsUsed.insert(index, true);
|
||||
|
||||
QString tagReplacer(4, TextCommand);
|
||||
|
@ -119,15 +119,15 @@ bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) {
|
|||
while (from < end && *from != '"' && *from != '}') {
|
||||
if (*from == '|') {
|
||||
if (from > start) subvarValue.append(start, int(from - start));
|
||||
if (countedIndex >= lngtags_max_counted_values) throw Exception(QString("Too many values inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
if (countedIndex >= lngtags_max_counted_values) throw Exception(QString("Too many values inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
||||
LangKey subkey = subkeyIndex(varKey, index, countedIndex++);
|
||||
if (subkey == lngkeys_cnt) {
|
||||
readingValue = false;
|
||||
warning(QString("Unexpected counted tag '%1' in key '%2', not using value.").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
warning(QString("Unexpected counted tag '%1' in key '%2', not using value.").arg(tagName).arg(varName));
|
||||
break;
|
||||
} else {
|
||||
if (feedingValue) {
|
||||
if (!feedKeyValue(subkey, QString::fromUtf8(subvarValue))) throw Exception(QString("Tag '%1' is not counted in key '%2'!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
if (!feedKeyValue(subkey, QString::fromUtf8(subvarValue))) throw Exception(QString("Tag '%1' is not counted in key '%2'!").arg(tagName).arg(varName));
|
||||
} else {
|
||||
foundKeyValue(subkey);
|
||||
}
|
||||
|
@ -137,10 +137,10 @@ bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) {
|
|||
start = from + 1;
|
||||
}
|
||||
if (*from == '\n') {
|
||||
throw Exception(QString("Unexpected end of string inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
throw Exception(QString("Unexpected end of string inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
||||
}
|
||||
if (*from == '\\') {
|
||||
if (from + 1 >= end) throw Exception(QString("Unexpected end of file inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
if (from + 1 >= end) throw Exception(QString("Unexpected end of file inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
||||
if (*(from + 1) == '"' || *(from + 1) == '\\' || *(from + 1) == '{' || *(from + 1) == '#') {
|
||||
if (from > start) subvarValue.append(start, int(from - start));
|
||||
start = ++from;
|
||||
|
@ -152,9 +152,9 @@ bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) {
|
|||
start = (++from) + 1;
|
||||
}
|
||||
} else if (*from == '{') {
|
||||
throw Exception(QString("Unexpected tag inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
throw Exception(QString("Unexpected tag inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
||||
} else if (*from == '#') {
|
||||
if (foundtag) throw Exception(QString("Replacement '#' double used inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
if (foundtag) throw Exception(QString("Replacement '#' double used inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
||||
foundtag = true;
|
||||
if (from > start) subvarValue.append(start, int(from - start));
|
||||
subvarValue.append(tagReplacer.toUtf8());
|
||||
|
@ -163,20 +163,20 @@ bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) {
|
|||
++from;
|
||||
}
|
||||
if (!readingValue) continue;
|
||||
if (from >= end) throw Exception(QString("Unexpected end of file inside counted tag '%1' in '%2' key!").arg(QString::fromUtf8(tagName)).arg(QString::fromUtf8(varName)));
|
||||
if (*from == '"') throw Exception(QString("Unexpected end of string inside counted tag '%1' in '%2' key!").arg(QString::fromUtf8(tagName)).arg(QString::fromUtf8(varName)));
|
||||
if (from >= end) throw Exception(QString("Unexpected end of file inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
||||
if (*from == '"') throw Exception(QString("Unexpected end of string inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
||||
|
||||
if (from > start) subvarValue.append(start, int(from - start));
|
||||
if (countedIndex >= lngtags_max_counted_values) throw Exception(QString("Too many values inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
if (countedIndex >= lngtags_max_counted_values) throw Exception(QString("Too many values inside counted tag '%1' in '%2' key!").arg(tagName).arg(varName));
|
||||
|
||||
LangKey subkey = subkeyIndex(varKey, index, countedIndex++);
|
||||
if (subkey == lngkeys_cnt) {
|
||||
readingValue = false;
|
||||
warning(QString("Unexpected counted tag '%1' in key '%2', not using value.").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
warning(QString("Unexpected counted tag '%1' in key '%2', not using value.").arg(tagName).arg(varName));
|
||||
break;
|
||||
} else {
|
||||
if (feedingValue) {
|
||||
if (!feedKeyValue(subkey, QString::fromUtf8(subvarValue))) throw Exception(QString("Tag '%1' is not counted in key '%2'!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
|
||||
if (!feedKeyValue(subkey, QString::fromUtf8(subvarValue))) throw Exception(QString("Tag '%1' is not counted in key '%2'!").arg(tagName).arg(varName));
|
||||
} else {
|
||||
foundKeyValue(subkey);
|
||||
}
|
||||
|
@ -186,17 +186,17 @@ bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) {
|
|||
}
|
||||
++from;
|
||||
}
|
||||
if (from >= end) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (from >= end) throw Exception(QString("Unexpected end of file in key '%1'!").arg(varName));
|
||||
if (readingValue && from > start) varValue.append(start, from - start);
|
||||
|
||||
if (!skipWhitespaces(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (*from != ';') throw Exception(QString("';' expected after \"value\" in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (!skipWhitespaces(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(varName));
|
||||
if (*from != ';') throw Exception(QString("';' expected after \"value\" in key '%1'!").arg(varName));
|
||||
|
||||
skipWhitespaces(++from, end);
|
||||
|
||||
if (readingValue) {
|
||||
if (feedingValue) {
|
||||
if (!feedKeyValue(varKey, QString::fromUtf8(varValue))) throw Exception(QString("Could not write value in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (!feedKeyValue(varKey, QString::fromUtf8(varValue))) throw Exception(QString("Could not write value in key '%1'!").arg(varName));
|
||||
} else {
|
||||
foundKeyValue(varKey);
|
||||
result.insert(varKey, QString::fromUtf8(varValue));
|
||||
|
|
|
@ -386,7 +386,8 @@ void Messenger::loadLanguage() {
|
|||
LOG(("Lang load warnings: %1").arg(loader.warnings()));
|
||||
}
|
||||
}
|
||||
QCoreApplication::instance()->installTranslator(_translator = new Translator());
|
||||
_translator = std::make_unique<Translator>();
|
||||
QCoreApplication::instance()->installTranslator(_translator.get());
|
||||
}
|
||||
|
||||
void Messenger::startLocalStorage() {
|
||||
|
@ -751,7 +752,6 @@ Messenger::~Messenger() {
|
|||
deinitLocationManager();
|
||||
|
||||
delete base::take(_uploader);
|
||||
delete base::take(_translator);
|
||||
|
||||
Window::Theme::Unload();
|
||||
|
||||
|
|
|
@ -154,8 +154,8 @@ private:
|
|||
|
||||
std::unique_ptr<MainWindow> _window;
|
||||
FileUploader *_uploader = nullptr;
|
||||
Translator *_translator = nullptr;
|
||||
|
||||
std::unique_ptr<Translator> _translator;
|
||||
std::unique_ptr<MTP::DcOptions> _dcOptions;
|
||||
std::unique_ptr<MTP::Instance> _mtproto;
|
||||
std::unique_ptr<MTP::Instance> _mtprotoForKeysDestroy;
|
||||
|
|
|
@ -75,9 +75,9 @@
|
|||
|
||||
'dependencies': [
|
||||
'codegen.gyp:codegen_emoji',
|
||||
'codegen.gyp:codegen_style',
|
||||
'codegen.gyp:codegen_lang',
|
||||
'codegen.gyp:codegen_numbers',
|
||||
'codegen.gyp:MetaLang',
|
||||
'codegen.gyp:codegen_style',
|
||||
'utils.gyp:Updater',
|
||||
],
|
||||
|
||||
|
|
|
@ -22,18 +22,10 @@
|
|||
'common.gypi',
|
||||
],
|
||||
'targets': [{
|
||||
'target_name': 'MetaLang',
|
||||
'target_name': 'codegen_lang',
|
||||
'variables': {
|
||||
'libs_loc': '../../../Libraries',
|
||||
'src_loc': '../SourceFiles',
|
||||
'gen_loc': '../GeneratedFiles',
|
||||
'mac_target': '10.10',
|
||||
'sources': [
|
||||
'<(src_loc)/_other/mlmain.cpp',
|
||||
'<(src_loc)/_other/mlmain.h',
|
||||
'<(src_loc)/_other/genlang.cpp',
|
||||
'<(src_loc)/_other/genlang.h',
|
||||
],
|
||||
},
|
||||
'includes': [
|
||||
'common_executable.gypi',
|
||||
|
@ -42,15 +34,33 @@
|
|||
|
||||
'include_dirs': [
|
||||
'<(src_loc)',
|
||||
'<(gen_loc)',
|
||||
],
|
||||
'sources': [
|
||||
'<!@(python <(DEPTH)/list_sources.py <@(sources) <(qt_moc_list_sources_arg))',
|
||||
'<(src_loc)/codegen/common/basic_tokenized_file.cpp',
|
||||
'<(src_loc)/codegen/common/basic_tokenized_file.h',
|
||||
'<(src_loc)/codegen/common/checked_utf8_string.cpp',
|
||||
'<(src_loc)/codegen/common/checked_utf8_string.h',
|
||||
'<(src_loc)/codegen/common/clean_file.cpp',
|
||||
'<(src_loc)/codegen/common/clean_file.h',
|
||||
'<(src_loc)/codegen/common/clean_file_reader.h',
|
||||
'<(src_loc)/codegen/common/const_utf8_string.h',
|
||||
'<(src_loc)/codegen/common/cpp_file.cpp',
|
||||
'<(src_loc)/codegen/common/cpp_file.h',
|
||||
'<(src_loc)/codegen/common/logging.cpp',
|
||||
'<(src_loc)/codegen/common/logging.h',
|
||||
'<(src_loc)/codegen/lang/generator.cpp',
|
||||
'<(src_loc)/codegen/lang/generator.h',
|
||||
'<(src_loc)/codegen/lang/main.cpp',
|
||||
'<(src_loc)/codegen/lang/options.cpp',
|
||||
'<(src_loc)/codegen/lang/options.h',
|
||||
'<(src_loc)/codegen/lang/parsed_file.cpp',
|
||||
'<(src_loc)/codegen/lang/parsed_file.h',
|
||||
'<(src_loc)/codegen/lang/processor.cpp',
|
||||
'<(src_loc)/codegen/lang/processor.h',
|
||||
],
|
||||
}, {
|
||||
'target_name': 'codegen_style',
|
||||
'variables': {
|
||||
'libs_loc': '../../../Libraries',
|
||||
'src_loc': '../SourceFiles',
|
||||
'mac_target': '10.10',
|
||||
},
|
||||
|
@ -92,7 +102,6 @@
|
|||
}, {
|
||||
'target_name': 'codegen_numbers',
|
||||
'variables': {
|
||||
'libs_loc': '../../../Libraries',
|
||||
'src_loc': '../SourceFiles',
|
||||
'mac_target': '10.10',
|
||||
},
|
||||
|
@ -130,7 +139,6 @@
|
|||
}, {
|
||||
'target_name': 'codegen_emoji',
|
||||
'variables': {
|
||||
'libs_loc': '../../../Libraries',
|
||||
'src_loc': '../SourceFiles',
|
||||
'mac_target': '10.10',
|
||||
},
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
}, {
|
||||
'action_name': 'codegen_lang',
|
||||
'inputs': [
|
||||
'<(PRODUCT_DIR)/MetaLang<(exe_ext)',
|
||||
'<(PRODUCT_DIR)/codegen_lang<(exe_ext)',
|
||||
'<(res_loc)/langs/lang.strings',
|
||||
],
|
||||
'outputs': [
|
||||
|
@ -88,9 +88,9 @@
|
|||
'<(SHARED_INTERMEDIATE_DIR)/lang_auto.h',
|
||||
],
|
||||
'action': [
|
||||
'<(PRODUCT_DIR)/MetaLang<(exe_ext)',
|
||||
'-lang_in', '<(res_loc)/langs/lang.strings',
|
||||
'-lang_out', '<(SHARED_INTERMEDIATE_DIR)/lang_auto',
|
||||
'<(PRODUCT_DIR)/codegen_lang<(exe_ext)',
|
||||
'-o<(SHARED_INTERMEDIATE_DIR)', '<(res_loc)/langs/lang.strings',
|
||||
'-w<(PRODUCT_DIR)/../..',
|
||||
],
|
||||
'message': 'codegen_lang-ing lang.strings..',
|
||||
'process_outputs_as_sources': 1,
|
||||
|
@ -107,6 +107,7 @@
|
|||
'action': [
|
||||
'<(PRODUCT_DIR)/codegen_numbers<(exe_ext)',
|
||||
'-o<(SHARED_INTERMEDIATE_DIR)', '<(res_loc)/numbers.txt',
|
||||
'-w<(PRODUCT_DIR)/../..',
|
||||
],
|
||||
'message': 'codegen_numbers-ing numbers.txt..',
|
||||
'process_outputs_as_sources': 1,
|
||||
|
|
Loading…
Reference in New Issue