Parsing of color and sprite values started in codegen_style.

This commit is contained in:
John Preston 2016-04-17 16:22:00 +03:00
parent 45bd2dc5fa
commit e0160f7d4c
10 changed files with 544 additions and 290 deletions

View File

@ -18,6 +18,8 @@ to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/ */
using "Resources/style_classes.txt";
defaultFontFamily: "Open Sans"; defaultFontFamily: "Open Sans";
semibold: "Open Sans Semibold"; semibold: "Open Sans Semibold";

View File

@ -45,7 +45,7 @@ bool isNameChar(char ch) {
} }
bool isWhitespaceChar(char ch) { bool isWhitespaceChar(char ch) {
return (ch == '\n' || ch == ' ' || ch == '\t'); return (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t');
} }
Token invalidToken() { Token invalidToken() {
@ -224,7 +224,7 @@ Type BasicTokenizedFile::readString() {
Type BasicTokenizedFile::readSingleLetter() { Type BasicTokenizedFile::readSingleLetter() {
auto type = singleLetterTokens_.value(reader_.currentChar(), Type::Invalid); auto type = singleLetterTokens_.value(reader_.currentChar(), Type::Invalid);
if (type == Type::Invalid) { if (type == Type::Invalid) {
reader_.logError(kErrorIncorrectToken, lineNumber_) << "incorrect token '" << reader_.currentChar() << "'."; reader_.logError(kErrorIncorrectToken, lineNumber_) << "incorrect token '" << reader_.currentChar() << "'";
return Type::Invalid; return Type::Invalid;
} }
@ -253,16 +253,12 @@ LogStream BasicTokenizedFile::logError(int code) const {
return reader_.logError(code, lineNumber_); return reader_.logError(code, lineNumber_);
} }
LogStream BasicTokenizedFile::logErrorUnexpectedToken(const std::string &expected) const { LogStream BasicTokenizedFile::logErrorUnexpectedToken() const {
std::string expectedTail;
if (!expected.empty()) {
expectedTail = ", expected " + expected;
}
if (currentToken_ < tokens_.size()) { if (currentToken_ < tokens_.size()) {
auto token = tokens_.at(currentToken_).original.toStdString(); auto token = tokens_.at(currentToken_).original.toStdString();
return logError(kErrorUnexpectedToken) << "unexpected token '" << token << '\'' << expectedTail << '.'; return logError(kErrorUnexpectedToken) << "unexpected token '" << token << "', expected ";
} }
return logError(kErrorUnexpectedToken) << "unexpected token" << expectedTail << '.'; return logError(kErrorUnexpectedToken) << "unexpected token, expected ";
} }
BasicTokenizedFile::~BasicTokenizedFile() = default; BasicTokenizedFile::~BasicTokenizedFile() = default;

View File

@ -21,8 +21,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once #pragma once
#include <memory> #include <memory>
#include <QtCore/QString>
#include <QtCore/QMap> #include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QList> #include <QtCore/QList>
#include "codegen/common/const_utf8_string.h" #include "codegen/common/const_utf8_string.h"
@ -89,7 +89,7 @@ public:
// Log error to std::cerr with 'code' at the current position in file. // Log error to std::cerr with 'code' at the current position in file.
LogStream logError(int code) const; LogStream logError(int code) const;
LogStream logErrorUnexpectedToken(const std::string &expected = std::string()) const; LogStream logErrorUnexpectedToken() const;
~BasicTokenizedFile(); ~BasicTokenizedFile();

View File

@ -29,29 +29,36 @@ namespace common {
// Wrapper around std::ostream that adds '\n' to the end of the logging line. // Wrapper around std::ostream that adds '\n' to the end of the logging line.
class LogStream { class LogStream {
public: public:
explicit LogStream(std::ostream &stream) : stream_(stream) { enum NullType {
Null,
};
explicit LogStream(NullType) : final_(false) {
}
explicit LogStream(std::ostream &stream) : stream_(&stream) {
} }
LogStream(LogStream &&other) : stream_(other.stream_) { LogStream(LogStream &&other) : stream_(other.stream_) {
other.final_ = false; other.final_ = false;
} }
std::ostream &stream() const { std::ostream *stream() const {
return stream_; return stream_;
} }
~LogStream() { ~LogStream() {
if (final_) { if (final_) {
stream_ << '\n'; *stream_ << '\n';
} }
} }
private: private:
std::ostream &stream_; std::ostream *stream_ = nullptr;
bool final_ = true; bool final_ = true;
}; };
template <typename T> template <typename T>
LogStream operator<<(LogStream &&stream, T &&value) { LogStream operator<<(LogStream &&stream, T &&value) {
stream.stream() << value; if (auto ostream = stream.stream()) {
*ostream << std::forward<T>(value);
}
return std::forward<LogStream>(stream); return std::forward<LogStream>(stream);
} }
@ -59,5 +66,7 @@ LogStream operator<<(LogStream &&stream, T &&value) {
// logError(kErrorFileTooLarge, filepath) << "file too large, size=" << size; // logError(kErrorFileTooLarge, filepath) << "file too large, size=" << size;
LogStream logError(int code, const QString &filepath, int line = 0); LogStream logError(int code, const QString &filepath, int line = 0);
static constexpr int kErrorInternal = 666;
} // namespace common } // namespace common
} // namespace codegen } // namespace codegen

View File

@ -22,8 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include <memory> #include <memory>
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QStringList> //#include "codegen/common/qstringlist_safe.h"
#include "codegen/style/options.h" #include "codegen/style/options.h"
namespace codegen { namespace codegen {

View File

@ -40,8 +40,8 @@ using common::logError;
Options parseOptions() { Options parseOptions() {
Options result; Options result;
auto args(QCoreApplication::instance()->arguments()); auto args(QCoreApplication::instance()->arguments());
for (auto i = args.cbegin(), e = args.cend(); i != e; ++i) { for (int i = 1, count = args.size(); i < count; ++i) { // skip first
const auto &arg(*i); const auto &arg(args.at(i));
// Rebuild all dependencies // Rebuild all dependencies
if (arg == "--rebuild") { if (arg == "--rebuild") {
@ -49,22 +49,22 @@ Options parseOptions() {
// Include paths // Include paths
} else if (arg == "-I") { } else if (arg == "-I") {
if (++i == e) { if (++i == count) {
logError(kErrorIncludePathExpected, "Command Line") << "include path expected after -I"; logError(kErrorIncludePathExpected, "Command Line") << "include path expected after -I";
return Options(); return Options();
} else { } else {
result.includePaths.push_back(*i); result.includePaths.push_back(args.at(i));
} }
} else if (arg.startsWith("-I")) { } else if (arg.startsWith("-I")) {
result.includePaths.push_back(arg.mid(2)); result.includePaths.push_back(arg.mid(2));
// Output path // Output path
} else if (arg == "-o") { } else if (arg == "-o") {
if (++i == e) { if (++i == count) {
logError(kErrorOutputPathExpected, "Command Line") << "output path expected after -o"; logError(kErrorOutputPathExpected, "Command Line") << "output path expected after -o";
return Options(); return Options();
} else { } else {
result.outputPath = *i; result.outputPath = args.at(i);
} }
} else if (arg.startsWith("-o")) { } else if (arg.startsWith("-o")) {
result.outputPath = arg.mid(2); result.outputPath = arg.mid(2);

View File

@ -21,13 +21,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "codegen/style/parsed_file.h" #include "codegen/style/parsed_file.h"
#include <iostream> #include <iostream>
#include <QtCore/QMap>
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtCore/QRegularExpression> #include <QtCore/QRegularExpression>
#include "codegen/common/basic_tokenized_file.h" #include "codegen/common/basic_tokenized_file.h"
#include "codegen/common/logging.h" #include "codegen/common/logging.h"
using Token = codegen::style::ParsedFile::Token;
using Type = Token::Type;
using BasicToken = codegen::common::BasicTokenizedFile::Token; using BasicToken = codegen::common::BasicTokenizedFile::Token;
using BasicType = BasicToken::Type; using BasicType = BasicToken::Type;
@ -35,12 +34,57 @@ namespace codegen {
namespace style { namespace style {
namespace { namespace {
QString plainValue(const BasicToken &token) { constexpr int kErrorInIncluded = 801;
constexpr int kErrorTypeMismatch = 802;
constexpr int kErrorUnknownField = 803;
constexpr int kErrorIdentifierNotFound = 804;
QString tokenValue(const BasicToken &token) {
if (token.type == BasicType::String) {
return token.value;
}
return token.original.toStringUnchecked(); return token.original.toStringUnchecked();
} }
Token invalidToken() { bool isValidColor(const QString &str) {
return { Type::Invalid, QString() }; auto len = str.size();
if (len != 3 && len != 4 && len != 6 && len != 8) {
return false;
}
for (auto ch : str) {
auto code = ch.unicode();
if ((code < '0' || code > '9') && (code < 'a' || code > 'f') && (code < 'A' || code > 'F')) {
return false;
}
}
return true;
}
std::string logFullName(const structure::FullName &name) {
return name.join('.').toStdString();
}
std::string logType(const structure::Type &type) {
if (type.tag == structure::TypeTag::Struct) {
return "struct " + logFullName(type.name);
}
static auto builtInTypes = new QMap<structure::TypeTag, std::string> {
{ structure::TypeTag::Int , "int" },
{ structure::TypeTag::Double , "double" },
{ structure::TypeTag::Pixels , "pixels" },
{ structure::TypeTag::String , "string" },
{ structure::TypeTag::Color , "color" },
{ structure::TypeTag::Point , "point" },
{ structure::TypeTag::Sprite , "sprite" },
{ structure::TypeTag::Size , "size" },
{ structure::TypeTag::Transition, "transition" },
{ structure::TypeTag::Cursor , "cursor" },
{ structure::TypeTag::Align , "align" },
{ structure::TypeTag::Margins , "margins" },
{ structure::TypeTag::Font , "font" },
};
return builtInTypes->value(type.tag, "invalid");
} }
} // namespace } // namespace
@ -55,246 +99,437 @@ bool ParsedFile::read() {
return false; return false;
} }
for (auto token = readInDefault(); token; token = readInDefault()) { bool noErrors = false;
if (token.type == Type::Using) { do {
auto includedOptions = options_; if (auto startToken = file_.getToken(BasicType::Name)) {
// includedOptions.inputPath = findIncludePath(token.value); if (tokenValue(startToken) == "using") {
ParsedFile included(includedOptions); if (auto includedResult = readIncluded()) {
if (!included.read()) { result_.includes.push_back(includedResult);
return false; continue;
}
result_.includes.push_back(included.data());
//} else if (token.type == Type::DefineStructStart) {
// if (!read)
}
}
return !failed();
}
Token ParsedFile::readToken() {
switch (state_) {
case State::Default: return readInDefault();
case State::StructStarted: return readInStructStarted();
case State::StructFieldName: return readInStructFieldName();
case State::Variable: return readInVariable();
case State::VariableParents: return readInVariableParents();
case State::VariableStarted: return readInVariableStarted();
case State::VariableChild: return readInVariableChild();
}
return invalidToken();
}
Token ParsedFile::readInDefault() {
if (auto startToken = file_.getToken(BasicType::Name)) {
if (plainValue(startToken) == "using") {
if (auto usingFile = file_.getToken(BasicType::String)) {
if (file_.getToken(BasicType::Semicolon)) {
return { Type::Using, usingFile.value };
} }
logErrorUnexpectedToken("';'"); } else if (auto braceOpen = file_.getToken(BasicType::LeftBrace)) {
if (auto structResult = readStruct(tokenValue(startToken))) {
result_.structs.push_back(structResult);
continue;
}
} else if (auto colonToken = file_.getToken(BasicType::Colon)) {
if (auto variableResult = readVariable(tokenValue(startToken))) {
result_.variables.push_back(variableResult);
continue;
}
}
}
if (!file_.atEnd()) {
logErrorUnexpectedToken() << "using keyword, or struct definition, or variable definition";
} else {
noErrors = !failed();
break;
}
} while (!failed());
if (noErrors) {
result_.fullpath = QFileInfo(options_.inputPath).absoluteFilePath();
}
return noErrors;
}
common::LogStream ParsedFile::logErrorTypeMismatch() {
return logError(kErrorTypeMismatch) << "type mismatch: ";
}
structure::Module ParsedFile::readIncluded() {
structure::Module result;
if (auto usingFile = file_.getToken(BasicType::String)) {
if (file_.getToken(BasicType::Semicolon)) {
ParsedFile included(includedOptions(tokenValue(usingFile)));
if (included.read()) {
result = included.data();
} else { } else {
logErrorUnexpectedToken("file path"); logError(kErrorInIncluded) << "error while parsing '" << tokenValue(usingFile).toStdString() << "'";
} }
return invalidToken(); } else {
} else if (auto braceOpen = file_.getToken(BasicType::LeftBrace)) { logErrorUnexpectedToken() << "';'";
state_ = State::StructStarted; }
return { Type::DefineStructStart, plainValue(startToken) }; } else {
} else if (auto colonToken = file_.getToken(BasicType::Colon)) { logErrorUnexpectedToken() << "file path";
state_ = State::Variable; }
return { Type::DefineVariable, plainValue(startToken) }; return result;
}
structure::Struct ParsedFile::readStruct(const QString &name) {
structure::Struct result = { composeFullName(name) };
do {
if (auto fieldName = file_.getToken(BasicType::Name)) {
if (auto field = readStructField(tokenValue(fieldName))) {
result.fields.push_back(field);
}
} else if (auto braceClose = file_.getToken(BasicType::RightBrace)) {
if (result.fields.isEmpty()) {
logErrorUnexpectedToken() << "at least one field in struct";
}
break;
} else {
logErrorUnexpectedToken() << "struct field name or '}'";
}
} while (!failed());
return result;
}
structure::Variable ParsedFile::readVariable(const QString &name) {
structure::Variable result = { composeFullName(name) };
if (auto value = readValue()) {
result.value = value;
if (!file_.getToken(BasicType::Semicolon)) {
logErrorUnexpectedToken() << "';'";
} }
} }
if (!file_.atEnd()) { return result;
logErrorUnexpectedToken("using keyword, or struct definition, or variable definition");
}
return invalidToken();
} }
Token ParsedFile::readInStructStarted() { structure::StructField ParsedFile::readStructField(const QString &name) {
if (auto fieldName = file_.getToken(BasicType::Name)) { structure::StructField result = { composeFullName(name) };
state_ = State::StructFieldName;
return { Type::DefineStructField, plainValue(fieldName) };
} else if (auto braceClose = file_.getToken(BasicType::RightBrace)) {
state_ = State::Default;
return { Type::DefineStructEnd };
}
logErrorUnexpectedToken("struct field name or '}'");
return invalidToken();
}
Token ParsedFile::readInStructFieldName() {
if (auto colonToken = file_.getToken(BasicType::Colon)) { if (auto colonToken = file_.getToken(BasicType::Colon)) {
if (auto fieldType = file_.getToken(BasicType::Name)) { if (auto type = readType()) {
if (file_.getToken(BasicType::Semicolon)) { result.type = type;
state_ = State::StructStarted; if (!file_.getToken(BasicType::Semicolon)) {
return { Type::StructFieldType, plainValue(fieldType) }; logErrorUnexpectedToken() << "';'";
} }
logErrorUnexpectedToken(";");
} else {
logErrorUnexpectedToken("struct field type name");
} }
} else { } else {
logErrorUnexpectedToken("':'"); logErrorUnexpectedToken() << "':'";
} }
return invalidToken(); return result;
} }
Token ParsedFile::readInVariable() { structure::Type ParsedFile::readType() {
if (auto value = readValueToken()) { structure::Type result;
if (file_.getToken(BasicType::Semicolon)) { if (auto nameToken = file_.getToken(BasicType::Name)) {
state_ = State::Default; auto name = tokenValue(nameToken);
return value; if (auto builtInType = typeNames_.value(name.toStdString())) {
result = builtInType;
} else {
result.tag = structure::TypeTag::Struct;
result.name = composeFullName(name);
} }
logErrorUnexpectedToken(";"); } else {
return invalidToken(); logErrorUnexpectedToken() << "type name";
}
if (failed()) {
return invalidToken();
} }
return result;
}
structure::Value ParsedFile::readValue() {
if (auto colorValue = readColorValue()) {
return colorValue;
} else if (auto pointValue = readPointValue()) {
return pointValue;
} else if (auto spriteValue = readSpriteValue()) {
return spriteValue;
} else if (auto sizeValue = readSizeValue()) {
return sizeValue;
} else if (auto transitionValue = readTransitionValue()) {
return transitionValue;
} else if (auto cursorValue = readCursorValue()) {
return cursorValue;
} else if (auto alignValue = readAlignValue()) {
return alignValue;
} else if (auto marginsValue = readMarginsValue()) {
return marginsValue;
} else if (auto fontValue = readFontValue()) {
return fontValue;
} else if (auto numericValue = readNumericValue()) {
return numericValue;
} else if (auto stringValue = readStringValue()) {
return stringValue;
} else if (auto structValue = readStructValue()) {
return structValue;
} else if (auto copyValue = readCopyValue()) {
return copyValue;
} else {
logErrorUnexpectedToken() << "variable value";
}
return {};
}
structure::Value ParsedFile::readStructValue() {
if (auto structName = file_.getToken(BasicType::Name)) { if (auto structName = file_.getToken(BasicType::Name)) {
if (file_.getToken(BasicType::LeftParenthesis)) { if (auto result = defaultConstructedStruct(composeFullName(tokenValue(structName)))) {
state_ = State::VariableParents; if (file_.getToken(BasicType::LeftParenthesis)) {
return { Type::StructStart, plainValue(structName) }; if (readStructParents(result)) {
} else if (file_.getToken(BasicType::LeftBrace)) { if (file_.getToken(BasicType::LeftBrace)) {
state_ = State::VariableStarted; readStructValueInner(result);
return { Type::StructStart, plainValue(structName) }; } else {
} else { logErrorUnexpectedToken() << "'{'";
logErrorUnexpectedToken("'(' or '{'"); }
} }
} else { } else if (file_.getToken(BasicType::LeftBrace)) {
logErrorUnexpectedToken("variable value"); readStructValueInner(result);
} } else {
return invalidToken(); logErrorUnexpectedToken() << "'(' or '{'";
}
Token ParsedFile::readInVariableParents() {
if (auto parentName = file_.getToken(BasicType::Name)) {
if (file_.getToken(BasicType::Comma)) {
return { Type::StructParent, plainValue(parentName) };
} else if (file_.getToken(BasicType::RightParenthesis)) {
if (file_.getToken(BasicType::LeftBrace)) {
state_ = State::VariableStarted;
return { Type::StructParent, plainValue(parentName) };
} }
logErrorUnexpectedToken("'{'"); return result;
}
file_.putBack();
}
return {};
}
structure::Value ParsedFile::defaultConstructedStruct(const structure::FullName &structName) {
structure::Value result;
for (const auto &structType : result_.structs) {
if (structType.name == structName) {
result.fields.reserve(structType.fields.size());
for (const auto &fieldType : structType.fields) {
result.fields.push_back({ fieldType.name, fieldType.type });
}
}
}
return result;
}
void ParsedFile::applyStructParent(structure::Value &result, const structure::FullName &parentName) {
for (const auto &structValue : result_.variables) {
if (structValue.name == parentName) {
if (structValue.value.type == result.type) {
const auto &srcFields(structValue.value.fields);
auto &dstFields(result.fields);
logAssert(srcFields.size() == dstFields.size()) << "struct size check failed";
for (int i = 0, s = srcFields.size(); i != s; ++i) {
logAssert(srcFields.at(i).value.type == dstFields.at(i).value.type) << "struct field type check failed";
dstFields[i].value = srcFields.at(i).value;
}
} else {
logErrorTypeMismatch() << "parent '" << logFullName(parentName) << "' has type '" << logType(structValue.value.type) << "' while child value has type " << logType(result.type);
}
}
}
}
bool ParsedFile::readStructValueInner(structure::Value &result) {
do {
if (auto fieldName = file_.getToken(BasicType::Name)) {
if (auto field = readVariable(tokenValue(fieldName))) {
for (auto &already : result.fields) {
if (already.name == field.name) {
if (already.value.type == field.value.type) {
already.value = field.value;
return true;
} else {
logErrorTypeMismatch() << "field '" << logFullName(already.name) << "' has type '" << logType(already.value.type) << "' while value has type " << logType(field.value.type);
return false;
}
}
}
logError(kErrorUnknownField) << "field '" << logFullName(field.name) << "' was not found in struct of type '" << logType(result.type) << "'";
}
} else if (file_.getToken(BasicType::RightBrace)) {
return true;
} else { } else {
logErrorUnexpectedToken("',' or ')'"); logErrorUnexpectedToken() << "variable field name or '}'";
} }
} else { } while (!failed());
logErrorUnexpectedToken("struct variable parent"); return false;
}
return invalidToken();
} }
Token ParsedFile::readInVariableStarted() { bool ParsedFile::readStructParents(structure::Value &result) {
if (auto fieldName = file_.getToken(BasicType::Name)) { do {
state_ = State::VariableChild; if (auto parentName = file_.getToken(BasicType::Name)) {
return { Type::VariableField, plainValue(fieldName) }; applyStructParent(result, composeFullName(tokenValue(parentName)));
} else if (auto braceClose = file_.getToken(BasicType::RightBrace)) { if (file_.getToken(BasicType::RightParenthesis)) {
state_ = State::Default; return true;
return { Type::StructEnd }; } else if (!file_.getToken(BasicType::Comma)) {
} logErrorUnexpectedToken() << "',' or ')'";
logErrorUnexpectedToken("struct variable field name or '}'"); }
return invalidToken(); } else {
} logErrorUnexpectedToken() << "struct variable parent";
Token ParsedFile::readInVariableChild() {
if (auto value = readValueToken()) {
if (file_.getToken(BasicType::Semicolon)) {
state_ = State::Default;
return value;
} }
logErrorUnexpectedToken(";"); } while (!failed());
} else { return false;
logErrorUnexpectedToken("variable field value");
}
return invalidToken();
} }
Token ParsedFile::readNumericToken() { //ParsedFile::Token ParsedFile::readInVariableChild() {
// if (auto value = readValue()) {
// if (file_.getToken(BasicType::Semicolon)) {
// state_ = State::Default;
// return value;
// }
// logErrorUnexpectedToken(";");
// } else {
// logErrorUnexpectedToken("variable field value");
// }
// return invalidToken();
//}
//
structure::Value ParsedFile::readPositiveValue() {
auto numericToken = file_.getAnyToken(); auto numericToken = file_.getAnyToken();
if (numericToken.type == BasicType::Int) { if (numericToken.type == BasicType::Int) {
return { Type::Int, plainValue(numericToken) }; return { { structure::TypeTag::Int }, tokenValue(numericToken) };
} else if (numericToken.type == BasicType::Double) { } else if (numericToken.type == BasicType::Double) {
return { Type::Double, plainValue(numericToken) }; return { { structure::TypeTag::Double }, tokenValue(numericToken) };
} else if (numericToken.type == BasicType::Name) { } else if (numericToken.type == BasicType::Name) {
auto value = plainValue(numericToken); auto value = tokenValue(numericToken);
auto match = QRegularExpression("^\\d+px$").match(value); auto match = QRegularExpression("^\\d+px$").match(value);
if (match.hasMatch()) { if (match.hasMatch()) {
return { Type::Pixels, value.mid(0, value.size() - 2) }; return { { structure::TypeTag::Pixels }, value.mid(0, value.size() - 2) };
} }
} }
file_.putBack(); file_.putBack();
return invalidToken(); return {};
} }
Token ParsedFile::readValueToken() { structure::Value ParsedFile::readNumericValue() {
if (auto colorValue = readColorToken()) { if (auto value = readPositiveValue()) {
return colorValue; return value;
} else if (auto pointValue = readPointToken()) {
return pointValue;
} else if (auto spriteValue = readSpriteToken()) {
return spriteValue;
} else if (auto sizeValue = readSizeToken()) {
return sizeValue;
} else if (auto transitionValue = readTransitionToken()) {
return transitionValue;
} else if (auto cursorValue = readCursorToken()) {
return cursorValue;
} else if (auto alignValue = readAlignToken()) {
return alignValue;
} else if (auto marginsValue = readMarginsToken()) {
return marginsValue;
} else if (auto fontValue = readFontToken()) {
return fontValue;
} else if (auto numericValue = readNumericToken()) {
return numericValue;
} else if (auto stringValue = file_.getToken(BasicType::String)) {
return { Type::String, stringValue.value };
} else if (auto minusToken = file_.getToken(BasicType::Minus)) { } else if (auto minusToken = file_.getToken(BasicType::Minus)) {
if (auto positiveValue = readNumericToken()) { if (auto positiveValue = readNumericValue()) {
return { positiveValue.type, '-' + positiveValue.value }; return { positiveValue.type, '-' + positiveValue.data };
} }
logErrorUnexpectedToken("numeric value"); logErrorUnexpectedToken() << "numeric value";
} }
return invalidToken(); return {};
} }
Token ParsedFile::readColorToken() { structure::Value ParsedFile::readStringValue() {
return invalidToken(); if (auto stringToken = file_.getToken(BasicType::String)) {
return { { structure::TypeTag::String }, stringToken.value };
}
return {};
} }
Token ParsedFile::readPointToken() { structure::Value ParsedFile::readColorValue() {
return invalidToken(); if (auto numberSign = file_.getToken(BasicType::Number)) {
auto color = file_.getAnyToken();
if (color.type == BasicType::Int || color.type == BasicType::Name) {
auto chars = tokenValue(color);
if (isValidColor(chars)) {
return { { structure::TypeTag::Color }, chars.toLower() };
}
} else {
logErrorUnexpectedToken() << "color value in #ccc, #ccca, #cccccc or #ccccccaa format";
}
}
return {};
} }
Token ParsedFile::readSpriteToken() { structure::Value ParsedFile::readPointValue() {
return invalidToken(); return {};
} }
Token ParsedFile::readSizeToken() { structure::Value ParsedFile::readSpriteValue() {
return invalidToken(); if (auto font = file_.getToken(BasicType::Name)) {
if (tokenValue(font) == "sprite") {
if (!file_.getToken(BasicType::LeftParenthesis)) {
logErrorUnexpectedToken() << "'(' and sprite definition";
return {};
}
auto x = readNumericValue(); file_.getToken(BasicType::Comma);
auto y = readNumericValue(); file_.getToken(BasicType::Comma);
auto w = readNumericValue(); file_.getToken(BasicType::Comma);
auto h = readNumericValue();
if (x.type.tag != structure::TypeTag::Pixels ||
y.type.tag != structure::TypeTag::Pixels ||
w.type.tag != structure::TypeTag::Pixels ||
h.type.tag != structure::TypeTag::Pixels) {
logErrorTypeMismatch() << "px rect for the sprite expected";
return {};
}
if (!file_.getToken(BasicType::RightParenthesis)) {
logErrorUnexpectedToken() << "')'";
return {};
}
return { { structure::TypeTag::Sprite }, x.data + ',' + y.data + ',' + w.data + ',' + h.data };
}
file_.putBack();
}
return {};
} }
Token ParsedFile::readTransitionToken() { structure::Value ParsedFile::readSizeValue() {
return invalidToken(); return {};
} }
Token ParsedFile::readCursorToken() { structure::Value ParsedFile::readTransitionValue() {
return invalidToken(); return {};
} }
Token ParsedFile::readAlignToken() { structure::Value ParsedFile::readCursorValue() {
return invalidToken(); return {};
} }
Token ParsedFile::readMarginsToken() { structure::Value ParsedFile::readAlignValue() {
return invalidToken(); return {};
} }
Token ParsedFile::readFontToken() { structure::Value ParsedFile::readMarginsValue() {
return invalidToken(); return {};
}
structure::Value ParsedFile::readFontValue() {
if (auto font = file_.getToken(BasicType::Name)) {
if (tokenValue(font) == "font") {
if (!file_.getToken(BasicType::LeftParenthesis)) {
logErrorUnexpectedToken() << "'(' and font definition";
return {};
}
structure::Value family, size;
do {
if (auto familyValue = readStringValue()) {
family = familyValue;
} else if (auto sizeValue = readNumericValue()) {
size = sizeValue;
} else if (auto copyValue = readCopyValue()) {
if (copyValue.type.tag == structure::TypeTag::String) {
family = copyValue;
} else if (copyValue.type.tag == structure::TypeTag::Pixels) {
size = copyValue;
} else {
logErrorUnexpectedToken() << "font family, font size or ')'";
}
} else if (file_.getToken(BasicType::RightParenthesis)) {
break;
} else {
logErrorUnexpectedToken() << "font family, font size or ')'";
}
} while (!failed());
if (size.type.tag != structure::TypeTag::Pixels) {
logErrorTypeMismatch() << "px value for the font size expected";
}
return { { structure::TypeTag::Font }, size.data + ',' + family.data };
}
file_.putBack();
}
return {};
}
structure::Value ParsedFile::readCopyValue() {
if (auto copyName = file_.getToken(BasicType::Name)) {
structure::FullName name = { tokenValue(copyName) };
for (const auto &variable : result_.variables) {
if (variable.name == name) {
auto result = variable.value;
result.copy = variable.name;
return result;
}
}
logError(kErrorIdentifierNotFound) << "identifier '" << logFullName(name) << "' not found";
}
return {};
}
Options ParsedFile::includedOptions(const QString &filepath) {
auto result = options_;
result.inputPath = filepath;
return result;
}
// Compose context-dependent full name.
structure::FullName ParsedFile::composeFullName(const QString &name) {
return { name };
} }
} // namespace style } // namespace style

View File

@ -21,8 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once #pragma once
#include <memory> #include <memory>
#include <QtCore/QString> #include <string>
#include <QtCore/QFileInfo>
#include "codegen/common/basic_tokenized_file.h" #include "codegen/common/basic_tokenized_file.h"
#include "codegen/style/options.h" #include "codegen/style/options.h"
#include "codegen/style/structure.h" #include "codegen/style/structure.h"
@ -37,45 +36,6 @@ public:
ParsedFile(const ParsedFile &other) = delete; ParsedFile(const ParsedFile &other) = delete;
ParsedFile &operator=(const ParsedFile &other) = delete; ParsedFile &operator=(const ParsedFile &other) = delete;
struct Token {
enum class Type {
Invalid,
Using, // value: file path
DefineStructStart, // value: struct name
DefineStructField, // value: struct field name
StructFieldType, // value: struct field type name
DefineStructEnd,
DefineVariable, // value: variable name
StructStart, // value: struct name
StructParent, // value: variable parent name
VariableField, // value: variable field name
StructEnd,
Int,
Double,
Pixels,
String,
Color,
Point,
Sprite,
Size,
Transition,
Cursor,
Align,
Margins,
Font,
};
Type type;
QString value;
explicit operator bool() const {
return type != Type::Invalid;
}
};
bool read(); bool read();
const structure::Module &data() const { const structure::Module &data() const {
@ -93,53 +53,75 @@ private:
failed_ = true; failed_ = true;
return file_.logError(code); return file_.logError(code);
} }
common::LogStream logErrorUnexpectedToken(const std::string &expected = std::string()) { common::LogStream logErrorUnexpectedToken() {
failed_ = true; failed_ = true;
return file_.logErrorUnexpectedToken(expected); return file_.logErrorUnexpectedToken();
}
common::LogStream logErrorTypeMismatch();
common::LogStream logAssert(bool assertion) {
if (!assertion) {
return logError(common::kErrorInternal) << "internal - ";
}
return common::LogStream(common::LogStream::Null);
} }
Token readToken(); // Helper methods for context-dependent reading.
structure::Module readIncluded();
structure::Struct readStruct(const QString &name);
structure::Variable readVariable(const QString &name);
// State value defines what are we waiting next. structure::StructField readStructField(const QString &name);
enum class State { structure::Type readType();
Default, // [ using | struct name | variable name | end ] structure::Value readValue();
StructStarted, // [ struct field name | struct end ]
StructFieldName, // [ struct field type ]
Variable, // [ struct name | variable value ]
VariableParents, // [ variable parent name | variable start ]
VariableStarted, // [ variable field name | variable end]
VariableChild, // [ variable child value ]
};
// Helper methods for readToken() being in specific State. structure::Value readStructValue();
Token readInDefault(); structure::Value defaultConstructedStruct(const structure::FullName &name);
Token readInStructStarted(); void applyStructParent(structure::Value &result, const structure::FullName &parentName);
Token readInStructFieldName(); bool readStructValueInner(structure::Value &result);
Token readInVariable(); bool readStructParents(structure::Value &result);
Token readInVariableParents();
Token readInVariableStarted();
Token readInVariableChild();
Token readNumericToken(); // Simple methods for reading value types.
Token readValueToken(); structure::Value readPositiveValue();
Token readColorToken(); structure::Value readNumericValue();
Token readPointToken(); structure::Value readStringValue();
Token readSpriteToken(); structure::Value readColorValue();
Token readSizeToken(); structure::Value readPointValue();
Token readTransitionToken(); structure::Value readSpriteValue();
Token readCursorToken(); structure::Value readSizeValue();
Token readAlignToken(); structure::Value readTransitionValue();
Token readMarginsToken(); structure::Value readCursorValue();
Token readFontToken(); structure::Value readAlignValue();
structure::Value readMarginsValue();
structure::Value readFontValue();
structure::Value readCopyValue();
// Look through include directories in options_ and find absolute include path.
Options includedOptions(const QString &filepath);
// Compose context-dependent full name.
structure::FullName composeFullName(const QString &name);
common::BasicTokenizedFile file_; common::BasicTokenizedFile file_;
Options options_; Options options_;
bool failed_ = false; bool failed_ = false;
State state_ = State::Default;
structure::Module result_; structure::Module result_;
QMap<std::string, structure::Type> typeNames_ = {
{ "int" , { structure::TypeTag::Int } },
{ "double" , { structure::TypeTag::Double } },
{ "pixels" , { structure::TypeTag::Pixels } },
{ "string" , { structure::TypeTag::String } },
{ "color" , { structure::TypeTag::Color } },
{ "point" , { structure::TypeTag::Point } },
{ "sprite" , { structure::TypeTag::Sprite } },
{ "size" , { structure::TypeTag::Size } },
{ "transition", { structure::TypeTag::Transition } },
{ "cursor" , { structure::TypeTag::Cursor } },
{ "align" , { structure::TypeTag::Align } },
{ "margins" , { structure::TypeTag::Margins } },
{ "font" , { structure::TypeTag::Font } },
};
}; };
} // namespace style } // namespace style

View File

@ -21,7 +21,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once #pragma once
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QList>
#include <QtCore/QStringList> #include <QtCore/QStringList>
namespace codegen { namespace codegen {
@ -32,6 +31,7 @@ namespace structure {
using FullName = QStringList; using FullName = QStringList;
enum class TypeTag { enum class TypeTag {
Invalid,
Int, Int,
Double, Double,
Pixels, Pixels,
@ -51,28 +51,55 @@ enum class TypeTag {
struct Type { struct Type {
TypeTag tag; TypeTag tag;
FullName name; // only for type == ClassType::Struct FullName name; // only for type == ClassType::Struct
explicit operator bool() const {
return (tag != TypeTag::Invalid);
}
}; };
inline bool operator==(const Type &a, const Type &b) {
return (a.tag == b.tag) && (a.name == b.name);
}
inline bool operator!=(const Type &a, const Type &b) {
return !(a == b);
}
struct Variable; struct Variable;
struct Value { struct Value {
Type type;
QString data; // for plain types QString data; // for plain types
QList<Variable> fields; // for struct types QList<Variable> fields; // for struct types
FullName copy; // for copies of existing named values
explicit operator bool() const {
return !data.isEmpty() || !fields.isEmpty() || !copy.isEmpty();
}
}; };
struct Variable { struct Variable {
FullName name; FullName name;
Type type;
Value value; Value value;
explicit operator bool() const {
return !name.isEmpty();
}
}; };
struct StructField { struct StructField {
FullName name; FullName name;
Type type; Type type;
explicit operator bool() const {
return !name.isEmpty();
}
}; };
struct Struct { struct Struct {
FullName name; FullName name;
QList<StructField> fields; QList<StructField> fields;
explicit operator bool() const {
return !name.isEmpty();
}
}; };
struct Module { struct Module {
@ -80,6 +107,10 @@ struct Module {
QList<Module> includes; QList<Module> includes;
QList<Struct> structs; QList<Struct> structs;
QList<Variable> variables; QList<Variable> variables;
explicit operator bool() const {
return !fullpath.isEmpty();
}
}; };
} // namespace structure } // namespace structure

View File

@ -69,7 +69,7 @@
</PropertyGroup> </PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile> <ClCompile>
<PreprocessorDefinitions>UNICODE;WIN32;QT_CORE_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>UNICODE;WIN32;QT_CORE_LIB;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>