Index: llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-bad-binary-1.rc =================================================================== --- llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-bad-binary-1.rc +++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-bad-binary-1.rc @@ -0,0 +1 @@ +LANGUAGE 0, & Index: llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-bad-binary-2.rc =================================================================== --- llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-bad-binary-2.rc +++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-bad-binary-2.rc @@ -0,0 +1 @@ +LANGUAGE 3||0, 0 Index: llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-bad-binary-3.rc =================================================================== --- llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-bad-binary-3.rc +++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-bad-binary-3.rc @@ -0,0 +1 @@ +LANGUAGE 3+-+3, 0 Index: llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-bad-unary.rc =================================================================== --- llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-bad-unary.rc +++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-bad-unary.rc @@ -0,0 +1 @@ +LANGUAGE 1~1, 0 Index: llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-unbalanced-1.rc =================================================================== --- llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-unbalanced-1.rc +++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-unbalanced-1.rc @@ -0,0 +1 @@ +LANGUAGE (1+2, 0 Index: llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-unbalanced-2.rc =================================================================== --- llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-unbalanced-2.rc +++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-unbalanced-2.rc @@ -0,0 +1 @@ +LANGUAGE 1+2)+3+4(, 0 Index: llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-unbalanced-3.rc =================================================================== --- llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-unbalanced-3.rc +++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr-unbalanced-3.rc @@ -0,0 +1 @@ +LANGUAGE (1+2+3)), 0 Index: llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr.rc =================================================================== --- llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr.rc +++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-expr.rc @@ -0,0 +1,15 @@ +LANGUAGE 3 + 2, 3 - 2 +LANGUAGE 3 | 2, 3 & 2 +LANGUAGE -3, ~3 +LANGUAGE 1|1&0, 0&0|1 +LANGUAGE 3+4-5, 3-4+5 +LANGUAGE 1+2|3, 3|1+2 +LANGUAGE 6&~5, 6&-8 +LANGUAGE -1, --1 +LANGUAGE ----1, -----1 +LANGUAGE ~1, ~~1 +LANGUAGE ~~~~1, ~~~~~1 +LANGUAGE 5-(1+2), 1|(1&0) +LANGUAGE ~(3-7), -(3+~7) +LANGUAGE 0, (1+3)|(2+11) +LANGUAGE (((((((5))))))), (((((((7))))))) Index: llvm/trunk/test/tools/llvm-rc/parser-expr.test =================================================================== --- llvm/trunk/test/tools/llvm-rc/parser-expr.test +++ llvm/trunk/test/tools/llvm-rc/parser-expr.test @@ -0,0 +1,52 @@ +; RUN: llvm-rc /V %p/Inputs/parser-expr.rc | FileCheck %s + +; CHECK: Language: 5, Sublanguage: 1 +; CHECK-NEXT: Language: 3, Sublanguage: 2 +; CHECK-NEXT: Language: 4294967293, Sublanguage: 4294967292 +; CHECK-NEXT: Language: 0, Sublanguage: 1 +; CHECK-NEXT: Language: 2, Sublanguage: 4 +; CHECK-NEXT: Language: 3, Sublanguage: 5 +; CHECK-NEXT: Language: 2, Sublanguage: 0 +; CHECK-NEXT: Language: 4294967295, Sublanguage: 1 +; CHECK-NEXT: Language: 1, Sublanguage: 4294967295 +; CHECK-NEXT: Language: 4294967294, Sublanguage: 1 +; CHECK-NEXT: Language: 1, Sublanguage: 4294967294 +; CHECK-NEXT: Language: 2, Sublanguage: 1 +; CHECK-NEXT: Language: 3, Sublanguage: 5 +; CHECK-NEXT: Language: 0, Sublanguage: 13 +; CHECK-NEXT: Language: 5, Sublanguage: 7 + + +; RUN: not llvm-rc /V %p/Inputs/parser-expr-bad-binary-1.rc 2>&1 | FileCheck %s --check-prefix BINARY1 + +; BINARY1: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got & + + +; RUN: not llvm-rc /V %p/Inputs/parser-expr-bad-binary-2.rc 2>&1 | FileCheck %s --check-prefix BINARY2 + +; BINARY2: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got | + + +; RUN: not llvm-rc /V %p/Inputs/parser-expr-bad-binary-3.rc 2>&1 | FileCheck %s --check-prefix BINARY3 + +; BINARY3: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got + + + +; RUN: not llvm-rc /V %p/Inputs/parser-expr-bad-unary.rc 2>&1 | FileCheck %s --check-prefix UNARY + +; UNARY: llvm-rc: Error parsing file: expected ',', got ~ + + +; RUN: not llvm-rc /V %p/Inputs/parser-expr-unbalanced-1.rc 2>&1 | FileCheck %s --check-prefix UNBALANCED1 + +; UNBALANCED1: llvm-rc: Error parsing file: expected ')', got , + + +; RUN: not llvm-rc /V %p/Inputs/parser-expr-unbalanced-2.rc 2>&1 | FileCheck %s --check-prefix UNBALANCED2 + +; UNBALANCED2: llvm-rc: Error parsing file: expected ',', got ) + + +; RUN: not llvm-rc /V %p/Inputs/parser-expr-unbalanced-3.rc 2>&1 | FileCheck %s --check-prefix UNBALANCED3 + +; UNBALANCED3: llvm-rc: Error parsing file: expected ',', got ) Index: llvm/trunk/test/tools/llvm-rc/parser.test =================================================================== --- llvm/trunk/test/tools/llvm-rc/parser.test +++ llvm/trunk/test/tools/llvm-rc/parser.test @@ -106,12 +106,12 @@ ; RUN: not llvm-rc /V %p/Inputs/parser-eof.rc 2>&1 | FileCheck %s --check-prefix PEOF -; PEOF: llvm-rc: Error parsing file: expected integer, got +; PEOF: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got ; RUN: not llvm-rc /V %p/Inputs/parser-no-characteristics-arg.rc 2>&1 | FileCheck %s --check-prefix PCHARACTERISTICS1 -; PCHARACTERISTICS1: llvm-rc: Error parsing file: expected integer, got BEGIN +; PCHARACTERISTICS1: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got BEGIN ; RUN: not llvm-rc /V %p/Inputs/parser-nonsense-token.rc 2>&1 | FileCheck %s --check-prefix PNONSENSE1 @@ -136,7 +136,7 @@ ; RUN: not llvm-rc /V %p/Inputs/parser-language-too-many-commas.rc 2>&1 | FileCheck %s --check-prefix PLANGUAGE2 -; PLANGUAGE2: llvm-rc: Error parsing file: expected integer, got , +; PLANGUAGE2: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got , ; RUN: not llvm-rc /V %p/Inputs/parser-html-bad-string.rc 2>&1 | FileCheck %s --check-prefix PHTML1 @@ -171,7 +171,7 @@ ; RUN: not llvm-rc /V %p/Inputs/parser-menu-bad-id.rc 2>&1 | FileCheck %s --check-prefix PMENU1 -; PMENU1: llvm-rc: Error parsing file: expected integer, got A +; PMENU1: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got A ; RUN: not llvm-rc /V %p/Inputs/parser-menu-bad-flag.rc 2>&1 | FileCheck %s --check-prefix PMENU2 @@ -211,7 +211,7 @@ ; RUN: not llvm-rc /V %p/Inputs/parser-dialog-unnecessary-string.rc 2>&1 | FileCheck %s --check-prefix PDIALOG5 -; PDIALOG5: llvm-rc: Error parsing file: expected integer, got "This shouldn't be here" +; PDIALOG5: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got "This shouldn't be here" ; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-wrong-fixed.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO1 Index: llvm/trunk/tools/llvm-rc/ResourceScriptParser.h =================================================================== --- llvm/trunk/tools/llvm-rc/ResourceScriptParser.h +++ llvm/trunk/tools/llvm-rc/ResourceScriptParser.h @@ -77,12 +77,18 @@ // 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 readInt(); // Parse an integer. Expected readString(); // Parse a string. Expected readIdentifier(); // Parse an identifier. Expected readIntOrString(); // Parse an integer or a string. Expected readTypeOrName(); // Parse an integer or an identifier. + // Helper integer expression parsing methods. + Expected parseIntExpr1(); + Expected parseIntExpr2(); + // Advance the state by one, discarding the current token. // If the discarded token had an incorrect type, fail. Error consumeType(Kind TokenKind); Index: llvm/trunk/tools/llvm-rc/ResourceScriptParser.cpp =================================================================== --- llvm/trunk/tools/llvm-rc/ResourceScriptParser.cpp +++ llvm/trunk/tools/llvm-rc/ResourceScriptParser.cpp @@ -107,10 +107,102 @@ CurLoc++; } -Expected 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: ( ) + - | & ~. 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 RCParser::readInt() { return parseIntExpr1(); } + +Expected 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 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 RCParser::readString() { Index: llvm/trunk/tools/llvm-rc/ResourceScriptToken.h =================================================================== --- llvm/trunk/tools/llvm-rc/ResourceScriptToken.h +++ llvm/trunk/tools/llvm-rc/ResourceScriptToken.h @@ -60,6 +60,9 @@ StringRef value() const; Kind kind() const; + // Check if a token describes a binary operator. + bool isBinaryOp() const; + private: Kind TokenKind; StringRef TokenValue; Index: llvm/trunk/tools/llvm-rc/ResourceScriptToken.cpp =================================================================== --- llvm/trunk/tools/llvm-rc/ResourceScriptToken.cpp +++ llvm/trunk/tools/llvm-rc/ResourceScriptToken.cpp @@ -60,6 +60,18 @@ 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("Error parsing file: " + message, inconvertibleErrorCode());