1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 02:52:53 +02:00

[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

llvm-svn: 314477
This commit is contained in:
Marek Sokolowski 2017-09-28 23:53:25 +00:00
parent 74a7f13015
commit 469da485f8
14 changed files with 174 additions and 9 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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;