From e0160f7d4c6f7eab72f22bbaa791711b17bdc0b1 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 17 Apr 2016 16:22:00 +0300 Subject: [PATCH] Parsing of color and sprite values started in codegen_style. --- Telegram/Resources/style.txt | 2 + .../codegen/common/basic_tokenized_file.cpp | 14 +- .../codegen/common/basic_tokenized_file.h | 4 +- Telegram/SourceFiles/codegen/common/logging.h | 19 +- .../SourceFiles/codegen/style/generator.h | 3 +- .../SourceFiles/codegen/style/options.cpp | 12 +- .../SourceFiles/codegen/style/parsed_file.cpp | 609 ++++++++++++------ .../SourceFiles/codegen/style/parsed_file.h | 134 ++-- .../SourceFiles/codegen/style/structure.h | 35 +- .../vc/codegen_style/codegen_style.vcxproj | 2 +- 10 files changed, 544 insertions(+), 290 deletions(-) diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index ec6871bf4..9c88277fd 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -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 Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ +using "Resources/style_classes.txt"; + defaultFontFamily: "Open Sans"; semibold: "Open Sans Semibold"; diff --git a/Telegram/SourceFiles/codegen/common/basic_tokenized_file.cpp b/Telegram/SourceFiles/codegen/common/basic_tokenized_file.cpp index 4bd5cf02e..fb6611e49 100644 --- a/Telegram/SourceFiles/codegen/common/basic_tokenized_file.cpp +++ b/Telegram/SourceFiles/codegen/common/basic_tokenized_file.cpp @@ -45,7 +45,7 @@ bool isNameChar(char ch) { } bool isWhitespaceChar(char ch) { - return (ch == '\n' || ch == ' ' || ch == '\t'); + return (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t'); } Token invalidToken() { @@ -224,7 +224,7 @@ Type BasicTokenizedFile::readString() { Type BasicTokenizedFile::readSingleLetter() { auto type = singleLetterTokens_.value(reader_.currentChar(), 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; } @@ -253,16 +253,12 @@ LogStream BasicTokenizedFile::logError(int code) const { return reader_.logError(code, lineNumber_); } -LogStream BasicTokenizedFile::logErrorUnexpectedToken(const std::string &expected) const { - std::string expectedTail; - if (!expected.empty()) { - expectedTail = ", expected " + expected; - } +LogStream BasicTokenizedFile::logErrorUnexpectedToken() const { if (currentToken_ < tokens_.size()) { 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; diff --git a/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h b/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h index 67b001736..97052783e 100644 --- a/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h +++ b/Telegram/SourceFiles/codegen/common/basic_tokenized_file.h @@ -21,8 +21,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include -#include #include +#include #include #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. LogStream logError(int code) const; - LogStream logErrorUnexpectedToken(const std::string &expected = std::string()) const; + LogStream logErrorUnexpectedToken() const; ~BasicTokenizedFile(); diff --git a/Telegram/SourceFiles/codegen/common/logging.h b/Telegram/SourceFiles/codegen/common/logging.h index 1aa1f1481..019cddd26 100644 --- a/Telegram/SourceFiles/codegen/common/logging.h +++ b/Telegram/SourceFiles/codegen/common/logging.h @@ -29,29 +29,36 @@ namespace common { // Wrapper around std::ostream that adds '\n' to the end of the logging line. class LogStream { 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_) { other.final_ = false; } - std::ostream &stream() const { + std::ostream *stream() const { return stream_; } ~LogStream() { if (final_) { - stream_ << '\n'; + *stream_ << '\n'; } } private: - std::ostream &stream_; + std::ostream *stream_ = nullptr; bool final_ = true; }; template LogStream operator<<(LogStream &&stream, T &&value) { - stream.stream() << value; + if (auto ostream = stream.stream()) { + *ostream << std::forward(value); + } return std::forward(stream); } @@ -59,5 +66,7 @@ LogStream operator<<(LogStream &&stream, T &&value) { // logError(kErrorFileTooLarge, filepath) << "file too large, size=" << size; LogStream logError(int code, const QString &filepath, int line = 0); +static constexpr int kErrorInternal = 666; + } // namespace common } // namespace codegen diff --git a/Telegram/SourceFiles/codegen/style/generator.h b/Telegram/SourceFiles/codegen/style/generator.h index dbb9601f6..ab6f2a0d3 100644 --- a/Telegram/SourceFiles/codegen/style/generator.h +++ b/Telegram/SourceFiles/codegen/style/generator.h @@ -22,8 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include #include -#include - +//#include "codegen/common/qstringlist_safe.h" #include "codegen/style/options.h" namespace codegen { diff --git a/Telegram/SourceFiles/codegen/style/options.cpp b/Telegram/SourceFiles/codegen/style/options.cpp index a7d880e17..4052b401d 100644 --- a/Telegram/SourceFiles/codegen/style/options.cpp +++ b/Telegram/SourceFiles/codegen/style/options.cpp @@ -40,8 +40,8 @@ using common::logError; Options parseOptions() { Options result; auto args(QCoreApplication::instance()->arguments()); - for (auto i = args.cbegin(), e = args.cend(); i != e; ++i) { - const auto &arg(*i); + for (int i = 1, count = args.size(); i < count; ++i) { // skip first + const auto &arg(args.at(i)); // Rebuild all dependencies if (arg == "--rebuild") { @@ -49,22 +49,22 @@ Options parseOptions() { // Include paths } else if (arg == "-I") { - if (++i == e) { + if (++i == count) { logError(kErrorIncludePathExpected, "Command Line") << "include path expected after -I"; return Options(); } else { - result.includePaths.push_back(*i); + result.includePaths.push_back(args.at(i)); } } else if (arg.startsWith("-I")) { result.includePaths.push_back(arg.mid(2)); // Output path } else if (arg == "-o") { - if (++i == e) { + if (++i == count) { logError(kErrorOutputPathExpected, "Command Line") << "output path expected after -o"; return Options(); } else { - result.outputPath = *i; + result.outputPath = args.at(i); } } else if (arg.startsWith("-o")) { result.outputPath = arg.mid(2); diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.cpp b/Telegram/SourceFiles/codegen/style/parsed_file.cpp index 4ca4aa701..b09144569 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.cpp +++ b/Telegram/SourceFiles/codegen/style/parsed_file.cpp @@ -21,13 +21,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "codegen/style/parsed_file.h" #include +#include #include #include #include "codegen/common/basic_tokenized_file.h" #include "codegen/common/logging.h" -using Token = codegen::style::ParsedFile::Token; -using Type = Token::Type; using BasicToken = codegen::common::BasicTokenizedFile::Token; using BasicType = BasicToken::Type; @@ -35,12 +34,57 @@ namespace codegen { namespace style { 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(); } -Token invalidToken() { - return { Type::Invalid, QString() }; +bool isValidColor(const QString &str) { + 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::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 @@ -55,246 +99,437 @@ bool ParsedFile::read() { return false; } - for (auto token = readInDefault(); token; token = readInDefault()) { - if (token.type == Type::Using) { - auto includedOptions = options_; -// includedOptions.inputPath = findIncludePath(token.value); - ParsedFile included(includedOptions); - if (!included.read()) { - return false; - } - 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 }; + bool noErrors = false; + do { + if (auto startToken = file_.getToken(BasicType::Name)) { + if (tokenValue(startToken) == "using") { + if (auto includedResult = readIncluded()) { + result_.includes.push_back(includedResult); + continue; } - 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 { - logErrorUnexpectedToken("file path"); + logError(kErrorInIncluded) << "error while parsing '" << tokenValue(usingFile).toStdString() << "'"; } - return invalidToken(); - } else if (auto braceOpen = file_.getToken(BasicType::LeftBrace)) { - state_ = State::StructStarted; - return { Type::DefineStructStart, plainValue(startToken) }; - } else if (auto colonToken = file_.getToken(BasicType::Colon)) { - state_ = State::Variable; - return { Type::DefineVariable, plainValue(startToken) }; + } else { + logErrorUnexpectedToken() << "';'"; + } + } else { + logErrorUnexpectedToken() << "file path"; + } + 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()) { - logErrorUnexpectedToken("using keyword, or struct definition, or variable definition"); - } - return invalidToken(); + return result; } -Token ParsedFile::readInStructStarted() { - if (auto fieldName = file_.getToken(BasicType::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() { +structure::StructField ParsedFile::readStructField(const QString &name) { + structure::StructField result = { composeFullName(name) }; if (auto colonToken = file_.getToken(BasicType::Colon)) { - if (auto fieldType = file_.getToken(BasicType::Name)) { - if (file_.getToken(BasicType::Semicolon)) { - state_ = State::StructStarted; - return { Type::StructFieldType, plainValue(fieldType) }; + if (auto type = readType()) { + result.type = type; + if (!file_.getToken(BasicType::Semicolon)) { + logErrorUnexpectedToken() << "';'"; } - logErrorUnexpectedToken(";"); - } else { - logErrorUnexpectedToken("struct field type name"); } } else { - logErrorUnexpectedToken("':'"); + logErrorUnexpectedToken() << "':'"; } - return invalidToken(); + return result; } -Token ParsedFile::readInVariable() { - if (auto value = readValueToken()) { - if (file_.getToken(BasicType::Semicolon)) { - state_ = State::Default; - return value; +structure::Type ParsedFile::readType() { + structure::Type result; + if (auto nameToken = file_.getToken(BasicType::Name)) { + auto name = tokenValue(nameToken); + if (auto builtInType = typeNames_.value(name.toStdString())) { + result = builtInType; + } else { + result.tag = structure::TypeTag::Struct; + result.name = composeFullName(name); } - logErrorUnexpectedToken(";"); - return invalidToken(); - } - if (failed()) { - return invalidToken(); + } else { + logErrorUnexpectedToken() << "type name"; } + 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 (file_.getToken(BasicType::LeftParenthesis)) { - state_ = State::VariableParents; - return { Type::StructStart, plainValue(structName) }; - } else if (file_.getToken(BasicType::LeftBrace)) { - state_ = State::VariableStarted; - return { Type::StructStart, plainValue(structName) }; - } else { - logErrorUnexpectedToken("'(' or '{'"); - } - } else { - logErrorUnexpectedToken("variable value"); - } - return invalidToken(); -} - -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) }; + if (auto result = defaultConstructedStruct(composeFullName(tokenValue(structName)))) { + if (file_.getToken(BasicType::LeftParenthesis)) { + if (readStructParents(result)) { + if (file_.getToken(BasicType::LeftBrace)) { + readStructValueInner(result); + } else { + logErrorUnexpectedToken() << "'{'"; + } + } + } else if (file_.getToken(BasicType::LeftBrace)) { + readStructValueInner(result); + } else { + logErrorUnexpectedToken() << "'(' or '{'"; } - 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 { - logErrorUnexpectedToken("',' or ')'"); + logErrorUnexpectedToken() << "variable field name or '}'"; } - } else { - logErrorUnexpectedToken("struct variable parent"); - } - return invalidToken(); + } while (!failed()); + return false; } -Token ParsedFile::readInVariableStarted() { - if (auto fieldName = file_.getToken(BasicType::Name)) { - state_ = State::VariableChild; - return { Type::VariableField, plainValue(fieldName) }; - } else if (auto braceClose = file_.getToken(BasicType::RightBrace)) { - state_ = State::Default; - return { Type::StructEnd }; - } - logErrorUnexpectedToken("struct variable field name or '}'"); - return invalidToken(); -} - -Token ParsedFile::readInVariableChild() { - if (auto value = readValueToken()) { - if (file_.getToken(BasicType::Semicolon)) { - state_ = State::Default; - return value; +bool ParsedFile::readStructParents(structure::Value &result) { + do { + if (auto parentName = file_.getToken(BasicType::Name)) { + applyStructParent(result, composeFullName(tokenValue(parentName))); + if (file_.getToken(BasicType::RightParenthesis)) { + return true; + } else if (!file_.getToken(BasicType::Comma)) { + logErrorUnexpectedToken() << "',' or ')'"; + } + } else { + logErrorUnexpectedToken() << "struct variable parent"; } - logErrorUnexpectedToken(";"); - } else { - logErrorUnexpectedToken("variable field value"); - } - return invalidToken(); + } while (!failed()); + return false; } -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(); if (numericToken.type == BasicType::Int) { - return { Type::Int, plainValue(numericToken) }; + return { { structure::TypeTag::Int }, tokenValue(numericToken) }; } else if (numericToken.type == BasicType::Double) { - return { Type::Double, plainValue(numericToken) }; + return { { structure::TypeTag::Double }, tokenValue(numericToken) }; } else if (numericToken.type == BasicType::Name) { - auto value = plainValue(numericToken); + auto value = tokenValue(numericToken); auto match = QRegularExpression("^\\d+px$").match(value); if (match.hasMatch()) { - return { Type::Pixels, value.mid(0, value.size() - 2) }; + return { { structure::TypeTag::Pixels }, value.mid(0, value.size() - 2) }; } } file_.putBack(); - return invalidToken(); + return {}; } -Token ParsedFile::readValueToken() { - if (auto colorValue = readColorToken()) { - return colorValue; - } 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 }; +structure::Value ParsedFile::readNumericValue() { + if (auto value = readPositiveValue()) { + return value; } else if (auto minusToken = file_.getToken(BasicType::Minus)) { - if (auto positiveValue = readNumericToken()) { - return { positiveValue.type, '-' + positiveValue.value }; + if (auto positiveValue = readNumericValue()) { + return { positiveValue.type, '-' + positiveValue.data }; } - logErrorUnexpectedToken("numeric value"); + logErrorUnexpectedToken() << "numeric value"; } - return invalidToken(); + return {}; } -Token ParsedFile::readColorToken() { - return invalidToken(); +structure::Value ParsedFile::readStringValue() { + if (auto stringToken = file_.getToken(BasicType::String)) { + return { { structure::TypeTag::String }, stringToken.value }; + } + return {}; } -Token ParsedFile::readPointToken() { - return invalidToken(); +structure::Value ParsedFile::readColorValue() { + 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() { - return invalidToken(); +structure::Value ParsedFile::readPointValue() { + return {}; } -Token ParsedFile::readSizeToken() { - return invalidToken(); +structure::Value ParsedFile::readSpriteValue() { + 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() { - return invalidToken(); +structure::Value ParsedFile::readSizeValue() { + return {}; } -Token ParsedFile::readCursorToken() { - return invalidToken(); +structure::Value ParsedFile::readTransitionValue() { + return {}; } -Token ParsedFile::readAlignToken() { - return invalidToken(); +structure::Value ParsedFile::readCursorValue() { + return {}; } -Token ParsedFile::readMarginsToken() { - return invalidToken(); +structure::Value ParsedFile::readAlignValue() { + return {}; } -Token ParsedFile::readFontToken() { - return invalidToken(); +structure::Value ParsedFile::readMarginsValue() { + 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 diff --git a/Telegram/SourceFiles/codegen/style/parsed_file.h b/Telegram/SourceFiles/codegen/style/parsed_file.h index 9d65f464b..a41efe946 100644 --- a/Telegram/SourceFiles/codegen/style/parsed_file.h +++ b/Telegram/SourceFiles/codegen/style/parsed_file.h @@ -21,8 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include -#include -#include +#include #include "codegen/common/basic_tokenized_file.h" #include "codegen/style/options.h" #include "codegen/style/structure.h" @@ -37,45 +36,6 @@ public: ParsedFile(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(); const structure::Module &data() const { @@ -93,53 +53,75 @@ private: failed_ = true; return file_.logError(code); } - common::LogStream logErrorUnexpectedToken(const std::string &expected = std::string()) { + common::LogStream logErrorUnexpectedToken() { 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. - enum class State { - Default, // [ using | struct name | variable name | end ] - 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 ] - }; + structure::StructField readStructField(const QString &name); + structure::Type readType(); + structure::Value readValue(); - // Helper methods for readToken() being in specific State. - Token readInDefault(); - Token readInStructStarted(); - Token readInStructFieldName(); - Token readInVariable(); - Token readInVariableParents(); - Token readInVariableStarted(); - Token readInVariableChild(); + structure::Value readStructValue(); + structure::Value defaultConstructedStruct(const structure::FullName &name); + void applyStructParent(structure::Value &result, const structure::FullName &parentName); + bool readStructValueInner(structure::Value &result); + bool readStructParents(structure::Value &result); - Token readNumericToken(); - Token readValueToken(); - Token readColorToken(); - Token readPointToken(); - Token readSpriteToken(); - Token readSizeToken(); - Token readTransitionToken(); - Token readCursorToken(); - Token readAlignToken(); - Token readMarginsToken(); - Token readFontToken(); + // Simple methods for reading value types. + structure::Value readPositiveValue(); + structure::Value readNumericValue(); + structure::Value readStringValue(); + structure::Value readColorValue(); + structure::Value readPointValue(); + structure::Value readSpriteValue(); + structure::Value readSizeValue(); + structure::Value readTransitionValue(); + structure::Value readCursorValue(); + 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_; Options options_; - bool failed_ = false; - State state_ = State::Default; - structure::Module result_; + QMap 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 diff --git a/Telegram/SourceFiles/codegen/style/structure.h b/Telegram/SourceFiles/codegen/style/structure.h index cde1813e8..c4f7b68c2 100644 --- a/Telegram/SourceFiles/codegen/style/structure.h +++ b/Telegram/SourceFiles/codegen/style/structure.h @@ -21,7 +21,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #pragma once #include -#include #include namespace codegen { @@ -32,6 +31,7 @@ namespace structure { using FullName = QStringList; enum class TypeTag { + Invalid, Int, Double, Pixels, @@ -51,28 +51,55 @@ enum class TypeTag { struct Type { TypeTag tag; 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 Value { + Type type; QString data; // for plain types QList 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 { FullName name; - Type type; Value value; + + explicit operator bool() const { + return !name.isEmpty(); + } }; struct StructField { FullName name; Type type; + + explicit operator bool() const { + return !name.isEmpty(); + } }; struct Struct { FullName name; QList fields; + + explicit operator bool() const { + return !name.isEmpty(); + } }; struct Module { @@ -80,6 +107,10 @@ struct Module { QList includes; QList structs; QList variables; + + explicit operator bool() const { + return !fullpath.isEmpty(); + } }; } // namespace structure diff --git a/Telegram/build/vc/codegen_style/codegen_style.vcxproj b/Telegram/build/vc/codegen_style/codegen_style.vcxproj index 967ae2811..848016fb3 100644 --- a/Telegram/build/vc/codegen_style/codegen_style.vcxproj +++ b/Telegram/build/vc/codegen_style/codegen_style.vcxproj @@ -69,7 +69,7 @@ - UNICODE;WIN32;QT_CORE_LIB;%(PreprocessorDefinitions) + UNICODE;WIN32;QT_CORE_LIB;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) Disabled ProgramDatabase MultiThreadedDebug