forked from OSchip/llvm-project
[TableGen] Add the !find bang operator
!find searches a source string for a target string and returns the position. Differential Revision: https://reviews.llvm.org/D101318
This commit is contained in:
parent
f0e848e63d
commit
952c6ddd8b
|
@ -218,13 +218,13 @@ TableGen provides "bang operators" that have a wide variety of uses:
|
|||
.. productionlist::
|
||||
BangOperator: one of
|
||||
: !add !and !cast !con !dag
|
||||
: !empty !eq !foldl !foreach !filter
|
||||
: !ge !getdagop !gt !head !if
|
||||
: !interleave !isa !le !listconcat !listsplat
|
||||
: !lt !mul !ne !not !or
|
||||
: !setdagop !shl !size !sra !srl
|
||||
: !strconcat !sub !subst !substr !tail
|
||||
: !xor
|
||||
: !empty !eq !filter !find !foldl
|
||||
: !foreach !ge !getdagop !gt !head
|
||||
: !if !interleave !isa !le !listconcat
|
||||
: !listsplat !lt !mul !ne !not
|
||||
: !or !setdagop !shl !size !sra
|
||||
: !srl !strconcat !sub !subst !substr
|
||||
: !tail !xor
|
||||
|
||||
The ``!cond`` operator has a slightly different
|
||||
syntax compared to other bang operators, so it is defined separately:
|
||||
|
@ -1639,6 +1639,12 @@ and non-0 as true.
|
|||
if the value is 0, the element is not included in the new list. If the value
|
||||
is anything else, the element is included.
|
||||
|
||||
``!find(``\ *string1*\ ``,`` *string2*\ [``,`` *start*]\ ``)``
|
||||
This operator searches for *string2* in *string1* and produces its
|
||||
position. The starting position of the search may be specified by *start*,
|
||||
which can range between 0 and the length of *string1*; the default is 0.
|
||||
If the string is not found, the result is -1.
|
||||
|
||||
``!foldl(``\ *init*\ ``,`` *list*\ ``,`` *acc*\ ``,`` *var*\ ``,`` *expr*\ ``)``
|
||||
This operator performs a left-fold over the items in *list*. The
|
||||
variable *acc* acts as the accumulator and is initialized to *init*.
|
||||
|
|
|
@ -862,7 +862,7 @@ public:
|
|||
/// !op (X, Y, Z) - Combine two inits.
|
||||
class TernOpInit : public OpInit, public FoldingSetNode {
|
||||
public:
|
||||
enum TernaryOp : uint8_t { SUBST, FOREACH, FILTER, IF, DAG, SUBSTR };
|
||||
enum TernaryOp : uint8_t { SUBST, FOREACH, FILTER, IF, DAG, SUBSTR, FIND };
|
||||
|
||||
private:
|
||||
Init *LHS, *MHS, *RHS;
|
||||
|
|
|
@ -1398,6 +1398,26 @@ Init *TernOpInit::Fold(Record *CurRec) const {
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FIND: {
|
||||
StringInit *LHSs = dyn_cast<StringInit>(LHS);
|
||||
StringInit *MHSs = dyn_cast<StringInit>(MHS);
|
||||
IntInit *RHSi = dyn_cast<IntInit>(RHS);
|
||||
if (LHSs && MHSs && RHSi) {
|
||||
int64_t SourceSize = LHSs->getValue().size();
|
||||
int64_t Start = RHSi->getValue();
|
||||
if (Start < 0 || Start > SourceSize)
|
||||
PrintError(CurRec->getLoc(),
|
||||
Twine("!find start position is out of range 0...") +
|
||||
std::to_string(SourceSize) + ": " +
|
||||
std::to_string(Start));
|
||||
auto I = LHSs->getValue().find(MHSs->getValue(), Start);
|
||||
if (I == std::string::npos)
|
||||
return IntInit::get(-1);
|
||||
return IntInit::get(I);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return const_cast<TernOpInit *>(this);
|
||||
|
@ -1443,6 +1463,7 @@ std::string TernOpInit::getAsString() const {
|
|||
case IF: Result = "!if"; break;
|
||||
case SUBST: Result = "!subst"; break;
|
||||
case SUBSTR: Result = "!substr"; break;
|
||||
case FIND: Result = "!find"; break;
|
||||
}
|
||||
return (Result + "(" +
|
||||
(UnquotedLHS ? LHS->getAsUnquotedString() : LHS->getAsString()) +
|
||||
|
|
|
@ -591,6 +591,7 @@ tgtok::TokKind TGLexer::LexExclaim() {
|
|||
.Case("strconcat", tgtok::XStrConcat)
|
||||
.Case("interleave", tgtok::XInterleave)
|
||||
.Case("substr", tgtok::XSubstr)
|
||||
.Case("find", tgtok::XFind)
|
||||
.Cases("setdagop", "setop", tgtok::XSetDagOp) // !setop is deprecated.
|
||||
.Cases("getdagop", "getop", tgtok::XGetDagOp) // !getop is deprecated.
|
||||
.Default(tgtok::Error);
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace tgtok {
|
|||
|
||||
// Bang operators.
|
||||
XConcat, XADD, XSUB, XMUL, XNOT, XAND, XOR, XXOR, XSRA, XSRL, XSHL,
|
||||
XListConcat, XListSplat, XStrConcat, XInterleave, XSubstr, XCast,
|
||||
XListConcat, XListSplat, XStrConcat, XInterleave, XSubstr, XFind, XCast,
|
||||
XSubst, XForEach, XFilter, XFoldl, XHead, XTail, XSize, XEmpty, XIf,
|
||||
XCond, XEq, XIsA, XDag, XNe, XLe, XLt, XGe, XGt, XSetDagOp, XGetDagOp,
|
||||
|
||||
|
|
|
@ -1508,6 +1508,9 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
|
|||
case tgtok::XSubstr:
|
||||
return ParseOperationSubstr(CurRec, ItemType);
|
||||
|
||||
case tgtok::XFind:
|
||||
return ParseOperationFind(CurRec, ItemType);
|
||||
|
||||
case tgtok::XCond:
|
||||
return ParseOperationCond(CurRec, ItemType);
|
||||
|
||||
|
@ -1756,6 +1759,94 @@ Init *TGParser::ParseOperationSubstr(Record *CurRec, RecTy *ItemType) {
|
|||
return (TernOpInit::get(Code, LHS, MHS, RHS, Type))->Fold(CurRec);
|
||||
}
|
||||
|
||||
/// Parse the !find operation. Return null on error.
|
||||
///
|
||||
/// Substr ::= !find(string, string [, start-int]) => int
|
||||
Init *TGParser::ParseOperationFind(Record *CurRec, RecTy *ItemType) {
|
||||
TernOpInit::TernaryOp Code = TernOpInit::FIND;
|
||||
RecTy *Type = IntRecTy::get();
|
||||
|
||||
Lex.Lex(); // eat the operation
|
||||
|
||||
if (!consume(tgtok::l_paren)) {
|
||||
TokError("expected '(' after !find operator");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Init *LHS = ParseValue(CurRec);
|
||||
if (!LHS)
|
||||
return nullptr;
|
||||
|
||||
if (!consume(tgtok::comma)) {
|
||||
TokError("expected ',' in !find operator");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SMLoc MHSLoc = Lex.getLoc();
|
||||
Init *MHS = ParseValue(CurRec);
|
||||
if (!MHS)
|
||||
return nullptr;
|
||||
|
||||
SMLoc RHSLoc = Lex.getLoc();
|
||||
Init *RHS;
|
||||
if (consume(tgtok::comma)) {
|
||||
RHSLoc = Lex.getLoc();
|
||||
RHS = ParseValue(CurRec);
|
||||
if (!RHS)
|
||||
return nullptr;
|
||||
} else {
|
||||
RHS = IntInit::get(0);
|
||||
}
|
||||
|
||||
if (!consume(tgtok::r_paren)) {
|
||||
TokError("expected ')' in !find operator");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ItemType && !Type->typeIsConvertibleTo(ItemType)) {
|
||||
Error(RHSLoc, Twine("expected value of type '") +
|
||||
ItemType->getAsString() + "', got '" +
|
||||
Type->getAsString() + "'");
|
||||
}
|
||||
|
||||
TypedInit *LHSt = dyn_cast<TypedInit>(LHS);
|
||||
if (!LHSt && !isa<UnsetInit>(LHS)) {
|
||||
TokError("could not determine type of the source string in !find");
|
||||
return nullptr;
|
||||
}
|
||||
if (LHSt && !isa<StringRecTy>(LHSt->getType())) {
|
||||
TokError(Twine("expected string, got type '") +
|
||||
LHSt->getType()->getAsString() + "'");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TypedInit *MHSt = dyn_cast<TypedInit>(MHS);
|
||||
if (!MHSt && !isa<UnsetInit>(MHS)) {
|
||||
TokError("could not determine type of the target string in !find");
|
||||
return nullptr;
|
||||
}
|
||||
if (MHSt && !isa<StringRecTy>(MHSt->getType())) {
|
||||
Error(MHSLoc, Twine("expected string, got type '") +
|
||||
MHSt->getType()->getAsString() + "'");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (RHS) {
|
||||
TypedInit *RHSt = dyn_cast<TypedInit>(RHS);
|
||||
if (!RHSt && !isa<UnsetInit>(RHS)) {
|
||||
TokError("could not determine type of the start position in !find");
|
||||
return nullptr;
|
||||
}
|
||||
if (RHSt && !isa<IntRecTy>(RHSt->getType())) {
|
||||
TokError(Twine("expected int, got type '") +
|
||||
RHSt->getType()->getAsString() + "'");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return (TernOpInit::get(Code, LHS, MHS, RHS, Type))->Fold(CurRec);
|
||||
}
|
||||
|
||||
/// Parse the !foreach and !filter operations. Return null on error.
|
||||
///
|
||||
/// ForEach ::= !foreach(ID, list-or-dag, expr) => list<expr type>
|
||||
|
@ -2282,7 +2373,8 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType,
|
|||
case tgtok::XForEach:
|
||||
case tgtok::XFilter:
|
||||
case tgtok::XSubst:
|
||||
case tgtok::XSubstr: { // Value ::= !ternop '(' Value ',' Value ',' Value ')'
|
||||
case tgtok::XSubstr:
|
||||
case tgtok::XFind: { // Value ::= !ternop '(' Value ',' Value ',' Value ')'
|
||||
return ParseOperation(CurRec, ItemType);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -262,6 +262,7 @@ private: // Parser methods.
|
|||
RecTy *ParseType();
|
||||
Init *ParseOperation(Record *CurRec, RecTy *ItemType);
|
||||
Init *ParseOperationSubstr(Record *CurRec, RecTy *ItemType);
|
||||
Init *ParseOperationFind(Record *CurRec, RecTy *ItemType);
|
||||
Init *ParseOperationForEachFilter(Record *CurRec, RecTy *ItemType);
|
||||
Init *ParseOperationCond(Record *CurRec, RecTy *ItemType);
|
||||
RecTy *ParseOperatorType();
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
// RUN: llvm-tblgen %s | FileCheck %s
|
||||
// RUN: not llvm-tblgen -DERROR1 %s 2>&1 | FileCheck --check-prefix=ERROR1 %s
|
||||
|
||||
// This file contains tests for the !find bang operator.
|
||||
|
||||
defvar Sentence = "This is the end of the world.";
|
||||
|
||||
// CHECK: def Rec01
|
||||
// CHECK-NEXT: int FirstThe = 8
|
||||
// CHECK-NEXT: int SameThe = 8
|
||||
// CHECK-NEXT: int SecondThe = 19
|
||||
// CHECK-NEXT: int ThirdThe = -1
|
||||
|
||||
def Rec01 {
|
||||
int FirstThe = !find(Sentence, "the");
|
||||
int SameThe = !find(Sentence, "the", FirstThe);
|
||||
int SecondThe = !find(Sentence, "the", !add(FirstThe, 1));
|
||||
int ThirdThe = !find(Sentence, "the", !add(SecondThe, 1));
|
||||
}
|
||||
|
||||
class C1<string name> {
|
||||
string Name = name;
|
||||
bit IsJr = !ne(!find(name, "Jr"), -1);
|
||||
}
|
||||
|
||||
// CHECK: def Rec02
|
||||
// CHECK-NEXT: string Name = "Sally Smith"
|
||||
// CHECK-NEXT: bit IsJr = 0
|
||||
// CHECK: def Rec03
|
||||
// CHECK-NEXT: string Name = "Fred Jones, Jr."
|
||||
// CHECK-NEXT: bit IsJr = 1
|
||||
|
||||
def Rec02 : C1<"Sally Smith">;
|
||||
def Rec03 : C1<"Fred Jones, Jr.">;
|
||||
|
||||
// CHECK: def Rec04
|
||||
// CHECK-NEXT: int ThisPos = 0
|
||||
// CHECK-NEXT: int WorldPos = 23
|
||||
// CHECK-NEXT: int TooLong = -1
|
||||
|
||||
def Rec04 {
|
||||
int ThisPos = !find(Sentence, "This");
|
||||
int WorldPos = !find(Sentence, "world.");
|
||||
int TooLong = !find(Sentence, "world.country");
|
||||
}
|
||||
|
||||
// CHECK: def Rec05
|
||||
// CHECK-NEXT: string Name = "Pat Snork"
|
||||
// CHECK-NEXT: bit IsJr = 0
|
||||
// CHECK-NEXT: bit JrOrSnork = 1
|
||||
|
||||
def Rec05 : C1<"Pat Snork"> {
|
||||
bit JrOrSnork = !or(IsJr, !ne(!find(Name, "Snork"), -1));
|
||||
}
|
||||
|
||||
#ifdef ERROR1
|
||||
|
||||
// ERROR1: !find start position is out of range 0...29: 100
|
||||
|
||||
def Rec06 {
|
||||
int Test1 = !find(Sentence, "the", 100);
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue