[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
This commit is contained in:
Marek Sokolowski 2017-09-28 23:53:25 +00:00
parent 3564b1df4b
commit 59066489d7
14 changed files with 196 additions and 9 deletions

View File

@ -0,0 +1 @@
LANGUAGE 0, &

View File

@ -0,0 +1 @@
LANGUAGE 3||0, 0

View File

@ -0,0 +1 @@
LANGUAGE 3+-+3, 0

View File

@ -0,0 +1 @@
LANGUAGE 1~1, 0

View File

@ -0,0 +1 @@
LANGUAGE (1+2, 0

View File

@ -0,0 +1 @@
LANGUAGE 1+2)+3+4(, 0

View File

@ -0,0 +1 @@
LANGUAGE (1+2+3)), 0

View File

@ -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)))))))

View File

@ -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 )

View File

@ -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 <EOF>
; PEOF: llvm-rc: Error parsing file: expected '-', '~', integer or '(', got <EOF>
; 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

View File

@ -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() {

View File

@ -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);

View File

@ -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());

View File

@ -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;