From 7ca5fcca7f11b917b1af2c1ea7462a81436eae6e Mon Sep 17 00:00:00 2001 From: Marek Sokolowski Date: Tue, 29 Aug 2017 16:49:59 +0000 Subject: [llvm-rc] Add DIALOG(EX) parsing ability (parser, pt 5/8). This extends the set of resources parsed by llvm-rc by DIALOG and DIALOGEX. Additionally, three optional resource statements specific to these two resources are added: CAPTION, FONT, and STYLE. Thanks for Nico Weber for his original work in this area. Differential Revision: https://reviews.llvm.org/D36905 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@312009 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/llvm-rc/ResourceScriptParser.cpp | 104 +++++++++++++++++++++++++++++++-- tools/llvm-rc/ResourceScriptParser.h | 7 +++ tools/llvm-rc/ResourceScriptStmt.cpp | 41 +++++++++++++ tools/llvm-rc/ResourceScriptStmt.h | 89 ++++++++++++++++++++++++++++ 4 files changed, 235 insertions(+), 6 deletions(-) (limited to 'tools/llvm-rc') diff --git a/tools/llvm-rc/ResourceScriptParser.cpp b/tools/llvm-rc/ResourceScriptParser.cpp index 5cc171c49af..499d0af83ad 100644 --- a/tools/llvm-rc/ResourceScriptParser.cpp +++ b/tools/llvm-rc/ResourceScriptParser.cpp @@ -67,6 +67,10 @@ RCParser::ParseType RCParser::parseSingleResource() { Result = parseAcceleratorsResource(); else if (TypeToken->equalsLower("CURSOR")) Result = parseCursorResource(); + else if (TypeToken->equalsLower("DIALOG")) + Result = parseDialogResource(false); + else if (TypeToken->equalsLower("DIALOGEX")) + Result = parseDialogResource(true); else if (TypeToken->equalsLower("ICON")) Result = parseIconResource(); else if (TypeToken->equalsLower("HTML")) @@ -235,17 +239,26 @@ Expected RCParser::parseOptionalStatements(bool IsExtended) { } Expected> -RCParser::parseSingleOptionalStatement(bool) { +RCParser::parseSingleOptionalStatement(bool IsExtended) { ASSIGN_OR_RETURN(TypeToken, readIdentifier()); if (TypeToken->equals_lower("CHARACTERISTICS")) return parseCharacteristicsStmt(); - else if (TypeToken->equals_lower("LANGUAGE")) + if (TypeToken->equals_lower("LANGUAGE")) return parseLanguageStmt(); - else if (TypeToken->equals_lower("VERSION")) + if (TypeToken->equals_lower("VERSION")) return parseVersionStmt(); - else - return getExpectedError("optional statement type, BEGIN or '{'", - /* IsAlreadyRead = */ true); + + if (IsExtended) { + if (TypeToken->equals_lower("CAPTION")) + return parseCaptionStmt(); + if (TypeToken->equals_lower("FONT")) + return parseFontStmt(); + if (TypeToken->equals_lower("STYLE")) + return parseStyleStmt(); + } + + return getExpectedError("optional statement type, BEGIN or '{'", + /* IsAlreadyRead = */ true); } RCParser::ParseType RCParser::parseLanguageResource() { @@ -277,6 +290,68 @@ RCParser::ParseType RCParser::parseCursorResource() { return make_unique(*Arg); } +RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) { + // Dialog resources have the following format of the arguments: + // DIALOG: x, y, width, height [opt stmts...] {controls...} + // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...} + // These are very similar, so we parse them together. + ASSIGN_OR_RETURN(LocResult, readIntsWithCommas(4, 4)); + + uint32_t HelpID = 0; // When HelpID is unset, it's assumed to be 0. + if (IsExtended && consumeOptionalType(Kind::Comma)) { + ASSIGN_OR_RETURN(HelpIDResult, readInt()); + HelpID = *HelpIDResult; + } + + ASSIGN_OR_RETURN(OptStatements, + parseOptionalStatements(/*UseExtendedStmts = */ true)); + + assert(isNextTokenKind(Kind::BlockBegin) && + "parseOptionalStatements, when successful, halts on BlockBegin."); + consume(); + + auto Dialog = make_unique( + (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3], + HelpID, std::move(*OptStatements), IsExtended); + + while (!consumeOptionalType(Kind::BlockEnd)) { + ASSIGN_OR_RETURN(ControlDefResult, parseControl()); + Dialog->addControl(std::move(*ControlDefResult)); + } + + return std::move(Dialog); +} + +Expected RCParser::parseControl() { + // Each control definition (except CONTROL) follows one of the schemes below + // depending on the control class: + // [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID] + // [class] id, x, y, width, height [, style] [, exstyle] [, helpID] + // Note that control ids must be integers. + ASSIGN_OR_RETURN(ClassResult, readIdentifier()); + StringRef ClassUpper = ClassResult->upper(); + if (Control::SupportedCtls.find(ClassUpper) == Control::SupportedCtls.end()) + return getExpectedError("control type, END or '}'", true); + + // Read caption if necessary. + StringRef Caption; + if (Control::CtlsWithTitle.find(ClassUpper) != Control::CtlsWithTitle.end()) { + ASSIGN_OR_RETURN(CaptionResult, readString()); + RETURN_IF_ERROR(consumeType(Kind::Comma)); + Caption = *CaptionResult; + } + + ASSIGN_OR_RETURN(Args, readIntsWithCommas(5, 8)); + + auto TakeOptArg = [&Args](size_t Id) -> Optional { + return Args->size() > Id ? (*Args)[Id] : Optional(); + }; + + return Control(*ClassResult, Caption, (*Args)[0], (*Args)[1], (*Args)[2], + (*Args)[3], (*Args)[4], TakeOptArg(5), TakeOptArg(6), + TakeOptArg(7)); +} + RCParser::ParseType RCParser::parseIconResource() { ASSIGN_OR_RETURN(Arg, readString()); return make_unique(*Arg); @@ -386,6 +461,23 @@ RCParser::ParseOptionType RCParser::parseVersionStmt() { return make_unique(*Arg); } +RCParser::ParseOptionType RCParser::parseCaptionStmt() { + ASSIGN_OR_RETURN(Arg, readString()); + return make_unique(*Arg); +} + +RCParser::ParseOptionType RCParser::parseFontStmt() { + ASSIGN_OR_RETURN(SizeResult, readInt()); + RETURN_IF_ERROR(consumeType(Kind::Comma)); + ASSIGN_OR_RETURN(NameResult, readString()); + return make_unique(*SizeResult, *NameResult); +} + +RCParser::ParseOptionType RCParser::parseStyleStmt() { + ASSIGN_OR_RETURN(Arg, readInt()); + return make_unique(*Arg); +} + Error RCParser::getExpectedError(const Twine Message, bool IsAlreadyRead) { return make_error( Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End); diff --git a/tools/llvm-rc/ResourceScriptParser.h b/tools/llvm-rc/ResourceScriptParser.h index 3fd1c27d928..bce2e0b544e 100644 --- a/tools/llvm-rc/ResourceScriptParser.h +++ b/tools/llvm-rc/ResourceScriptParser.h @@ -128,11 +128,15 @@ private: ParseType parseLanguageResource(); ParseType parseAcceleratorsResource(); ParseType parseCursorResource(); + ParseType parseDialogResource(bool IsExtended); ParseType parseIconResource(); ParseType parseHTMLResource(); ParseType parseMenuResource(); ParseType parseStringTableResource(); + // Helper DIALOG parser - a single control. + Expected parseControl(); + // Helper MENU parser. Expected parseMenuItemsList(); @@ -140,6 +144,9 @@ private: ParseOptionType parseLanguageStmt(); ParseOptionType parseCharacteristicsStmt(); ParseOptionType parseVersionStmt(); + ParseOptionType parseCaptionStmt(); + ParseOptionType parseFontStmt(); + ParseOptionType parseStyleStmt(); // Raises an error. If IsAlreadyRead = false (default), this complains about // the token that couldn't be parsed. If the flag is on, this complains about diff --git a/tools/llvm-rc/ResourceScriptStmt.cpp b/tools/llvm-rc/ResourceScriptStmt.cpp index 9f61ce451de..cfbd2f8f7a3 100644 --- a/tools/llvm-rc/ResourceScriptStmt.cpp +++ b/tools/llvm-rc/ResourceScriptStmt.cpp @@ -113,6 +113,35 @@ raw_ostream &StringTableResource::log(raw_ostream &OS) const { return OS; } +const StringSet<> Control::SupportedCtls = { + "LTEXT", "RTEXT", "CTEXT", "PUSHBUTTON", "DEFPUSHBUTTON", "EDITTEXT"}; + +const StringSet<> Control::CtlsWithTitle = {"LTEXT", "RTEXT", "CTEXT", + "PUSHBUTTON", "DEFPUSHBUTTON"}; + +raw_ostream &Control::log(raw_ostream &OS) const { + OS << " Control (" << ID << "): " << Type << ", title: " << Title + << ", loc: (" << X << ", " << Y << "), size: [" << Width << ", " << Height + << "]"; + if (Style) + OS << ", style: " << *Style; + if (ExtStyle) + OS << ", ext. style: " << *ExtStyle; + if (HelpID) + OS << ", help ID: " << *HelpID; + return OS << "\n"; +} + +raw_ostream &DialogResource::log(raw_ostream &OS) const { + OS << "Dialog" << (IsExtended ? "Ex" : "") << " (" << ResName << "): loc: (" + << X << ", " << Y << "), size: [" << Width << ", " << Height + << "], help ID: " << HelpID << "\n"; + OptStatements.log(OS); + for (auto &Ctl : Controls) + Ctl.log(OS); + return OS; +} + raw_ostream &CharacteristicsStmt::log(raw_ostream &OS) const { return OS << "Characteristics: " << Value << "\n"; } @@ -121,5 +150,17 @@ raw_ostream &VersionStmt::log(raw_ostream &OS) const { return OS << "Version: " << Value << "\n"; } +raw_ostream &CaptionStmt::log(raw_ostream &OS) const { + return OS << "Caption: " << Value << "\n"; +} + +raw_ostream &FontStmt::log(raw_ostream &OS) const { + return OS << "Font: size = " << Size << ", face = " << Typeface << "\n"; +} + +raw_ostream &StyleStmt::log(raw_ostream &OS) const { + return OS << "Style: " << Value << "\n"; +} + } // namespace rc } // namespace llvm diff --git a/tools/llvm-rc/ResourceScriptStmt.h b/tools/llvm-rc/ResourceScriptStmt.h index 7166138a517..0812c263b98 100644 --- a/tools/llvm-rc/ResourceScriptStmt.h +++ b/tools/llvm-rc/ResourceScriptStmt.h @@ -16,6 +16,8 @@ #include "ResourceScriptToken.h" +#include "llvm/ADT/StringSet.h" + namespace llvm { namespace rc { @@ -268,6 +270,55 @@ public: raw_ostream &log(raw_ostream &) const override; }; +// -- DIALOG(EX) resource and its helper classes -- +// +// This resource describes dialog boxes and controls residing inside them. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381003(v=vs.85).aspx +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381002(v=vs.85).aspx + +// Single control definition. +class Control { + StringRef Type, Title; + uint32_t ID, X, Y, Width, Height; + Optional Style, ExtStyle, HelpID; + +public: + Control(StringRef CtlType, StringRef CtlTitle, uint32_t CtlID, uint32_t PosX, + uint32_t PosY, uint32_t ItemWidth, uint32_t ItemHeight, + Optional ItemStyle, Optional ExtItemStyle, + Optional CtlHelpID) + : Type(CtlType), Title(CtlTitle), ID(CtlID), X(PosX), Y(PosY), + Width(ItemWidth), Height(ItemHeight), Style(ItemStyle), + ExtStyle(ExtItemStyle), HelpID(CtlHelpID) {} + + static const StringSet<> SupportedCtls; + static const StringSet<> CtlsWithTitle; + + raw_ostream &log(raw_ostream &) const; +}; + +// Single dialog definition. We don't create distinct classes for DIALOG and +// DIALOGEX because of their being too similar to each other. We only have a +// flag determining the type of the dialog box. +class DialogResource : public RCResource { + uint32_t X, Y, Width, Height, HelpID; + OptionalStmtList OptStatements; + std::vector Controls; + bool IsExtended; + +public: + DialogResource(uint32_t PosX, uint32_t PosY, uint32_t DlgWidth, + uint32_t DlgHeight, uint32_t DlgHelpID, + OptionalStmtList &&OptStmts, bool IsDialogEx) + : X(PosX), Y(PosY), Width(DlgWidth), Height(DlgHeight), HelpID(DlgHelpID), + OptStatements(std::move(OptStmts)), IsExtended(IsDialogEx) {} + + void addControl(Control &&Ctl) { Controls.push_back(std::move(Ctl)); } + + raw_ostream &log(raw_ostream &) const override; +}; + // CHARACTERISTICS optional statement. // // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380872(v=vs.85).aspx @@ -290,6 +341,44 @@ public: raw_ostream &log(raw_ostream &) const override; }; +// CAPTION optional statement. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380778(v=vs.85).aspx +class CaptionStmt : public OptionalStmt { + StringRef Value; + +public: + CaptionStmt(StringRef Caption) : Value(Caption) {} + raw_ostream &log(raw_ostream &) const override; +}; + +// FONT optional statement. +// Note that the documentation is inaccurate: it expects five arguments to be +// given, however the example provides only two. In fact, the original tool +// expects two arguments - point size and name of the typeface. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381013(v=vs.85).aspx +class FontStmt : public OptionalStmt { + uint32_t Size; + StringRef Typeface; + +public: + FontStmt(uint32_t FontSize, StringRef FontName) + : Size(FontSize), Typeface(FontName) {} + raw_ostream &log(raw_ostream &) const override; +}; + +// STYLE optional statement. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381051(v=vs.85).aspx +class StyleStmt : public OptionalStmt { + uint32_t Value; + +public: + StyleStmt(uint32_t Style) : Value(Style) {} + raw_ostream &log(raw_ostream &) const override; +}; + } // namespace rc } // namespace llvm -- cgit v1.2.3