[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:
Paul C. Anagnostopoulos 2021-04-26 09:53:35 -04:00
parent f0e848e63d
commit 952c6ddd8b
8 changed files with 195 additions and 10 deletions

View File

@ -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*.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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