diff options
author | Marek Sokolowski <mnbvmar@gmail.com> | 2017-09-28 23:53:25 +0000 |
---|---|---|
committer | Marek Sokolowski <mnbvmar@gmail.com> | 2017-09-28 23:53:25 +0000 |
commit | 59066489d76dfc432a88a86e2fdda7f89e8977a5 (patch) | |
tree | 2c9a99e8811d01b62f74f0f3c0298c2686c1f90b /tools/llvm-rc | |
parent | 3564b1df4b8f4a4e4a256d6ad89863cb5da953d8 (diff) |
[llvm-rc] Add integer expressions parsing ability. [7/8]
This allows the ints to be written as integer expressions evaluating to
unsigned 16-bit/32-bit integers.
All the expressions may use the following operators: + - & | ~, and
parentheses. Minus token - can be also unary. There is no precedence of
the operators other than the unary operators binding stronger than their
binary counterparts.
Differential Revision: https://reviews.llvm.org/D37022
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@314477 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools/llvm-rc')
-rw-r--r-- | tools/llvm-rc/ResourceScriptParser.cpp | 100 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceScriptParser.h | 6 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceScriptToken.cpp | 12 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceScriptToken.h | 3 |
4 files changed, 117 insertions, 4 deletions
diff --git a/tools/llvm-rc/ResourceScriptParser.cpp b/tools/llvm-rc/ResourceScriptParser.cpp index 854b9202d96..ee7de6b937a 100644 --- a/tools/llvm-rc/ResourceScriptParser.cpp +++ b/tools/llvm-rc/ResourceScriptParser.cpp @@ -107,10 +107,102 @@ void RCParser::consume() { CurLoc++; } -Expected<uint32_t> RCParser::readInt() { - if (!isNextTokenKind(Kind::Int)) - return getExpectedError("integer"); - return read().intValue(); +// An integer description might consist of a single integer or +// an arithmetic expression evaluating to the integer. The expressions +// can contain the following tokens: <int> ( ) + - | & ~. Their meaning +// is the same as in C++. +// The operators in the original RC implementation have the following +// precedence: +// 1) Unary operators (- ~), +// 2) Binary operators (+ - & |), with no precedence. +// +// The following grammar is used to parse the expressions Exp1: +// Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2 +// Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1). +// (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions, +// separated by binary operators.) +// +// Expressions of type Exp1 are read by parseIntExpr1(Inner) method, while Exp2 +// is read by parseIntExpr2(). +// +// The original Microsoft tool handles multiple unary operators incorrectly. +// For example, in 16-bit little-endian integers: +// 1 => 01 00, -1 => ff ff, --1 => ff ff, ---1 => 01 00; +// 1 => 01 00, ~1 => fe ff, ~~1 => fd ff, ~~~1 => fc ff. +// Our implementation differs from the original one and handles these +// operators correctly: +// 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff; +// 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff. + +Expected<uint32_t> RCParser::readInt() { return parseIntExpr1(); } + +Expected<uint32_t> RCParser::parseIntExpr1() { + // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2. + ASSIGN_OR_RETURN(FirstResult, parseIntExpr2()); + uint32_t Result = *FirstResult; + + while (!isEof() && look().isBinaryOp()) { + auto OpToken = read(); + ASSIGN_OR_RETURN(NextResult, parseIntExpr2()); + + switch (OpToken.kind()) { + case Kind::Plus: + Result += *NextResult; + break; + + case Kind::Minus: + Result -= *NextResult; + break; + + case Kind::Pipe: + Result |= *NextResult; + break; + + case Kind::Amp: + Result &= *NextResult; + break; + + default: + llvm_unreachable("Already processed all binary ops."); + } + } + + return Result; +} + +Expected<uint32_t> RCParser::parseIntExpr2() { + // Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1). + static const char ErrorMsg[] = "'-', '~', integer or '('"; + + if (isEof()) + return getExpectedError(ErrorMsg); + + switch (look().kind()) { + case Kind::Minus: { + consume(); + ASSIGN_OR_RETURN(Result, parseIntExpr2()); + return -(*Result); + } + + case Kind::Tilde: { + consume(); + ASSIGN_OR_RETURN(Result, parseIntExpr2()); + return ~(*Result); + } + + case Kind::Int: + return read().intValue(); + + case Kind::LeftParen: { + consume(); + ASSIGN_OR_RETURN(Result, parseIntExpr1()); + RETURN_IF_ERROR(consumeType(Kind::RightParen)); + return *Result; + } + + default: + return getExpectedError(ErrorMsg); + } } Expected<StringRef> RCParser::readString() { diff --git a/tools/llvm-rc/ResourceScriptParser.h b/tools/llvm-rc/ResourceScriptParser.h index 132106f0c4e..56042f79d5f 100644 --- a/tools/llvm-rc/ResourceScriptParser.h +++ b/tools/llvm-rc/ResourceScriptParser.h @@ -77,12 +77,18 @@ private: // The following methods try to read a single token, check if it has the // correct type and then parse it. + // Each integer can be written as an arithmetic expression producing an + // unsigned 32-bit integer. Expected<uint32_t> readInt(); // Parse an integer. Expected<StringRef> readString(); // Parse a string. Expected<StringRef> readIdentifier(); // Parse an identifier. Expected<IntOrString> readIntOrString(); // Parse an integer or a string. Expected<IntOrString> readTypeOrName(); // Parse an integer or an identifier. + // Helper integer expression parsing methods. + Expected<uint32_t> parseIntExpr1(); + Expected<uint32_t> parseIntExpr2(); + // Advance the state by one, discarding the current token. // If the discarded token had an incorrect type, fail. Error consumeType(Kind TokenKind); diff --git a/tools/llvm-rc/ResourceScriptToken.cpp b/tools/llvm-rc/ResourceScriptToken.cpp index ba1ed5d416a..df9e3d9caab 100644 --- a/tools/llvm-rc/ResourceScriptToken.cpp +++ b/tools/llvm-rc/ResourceScriptToken.cpp @@ -60,6 +60,18 @@ StringRef RCToken::value() const { return TokenValue; } Kind RCToken::kind() const { return TokenKind; } +bool RCToken::isBinaryOp() const { + switch (TokenKind) { + case Kind::Plus: + case Kind::Minus: + case Kind::Pipe: + case Kind::Amp: + return true; + default: + return false; + } +} + static Error getStringError(const Twine &message) { return make_error<StringError>("Error parsing file: " + message, inconvertibleErrorCode()); diff --git a/tools/llvm-rc/ResourceScriptToken.h b/tools/llvm-rc/ResourceScriptToken.h index 268f37a9d00..6846e096848 100644 --- a/tools/llvm-rc/ResourceScriptToken.h +++ b/tools/llvm-rc/ResourceScriptToken.h @@ -60,6 +60,9 @@ public: StringRef value() const; Kind kind() const; + // Check if a token describes a binary operator. + bool isBinaryOp() const; + private: Kind TokenKind; StringRef TokenValue; |