summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul C. Anagnostopoulos <paul@windfall.com>2021-04-26 09:53:35 -0400
committerPaul C. Anagnostopoulos <paul@windfall.com>2021-04-28 09:51:00 -0400
commit952c6ddd8b32a0dc4a65147e20999428191950f0 (patch)
treebb1590f6cd8093644862247e8eaa53532972e2c0
parentf0e848e63d99d9c13c7627dab013c05227d8b8f5 (diff)
downloadllvm-952c6ddd8b32a0dc4a65147e20999428191950f0.tar.gz
[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
-rw-r--r--llvm/docs/TableGen/ProgRef.rst20
-rw-r--r--llvm/include/llvm/TableGen/Record.h2
-rw-r--r--llvm/lib/TableGen/Record.cpp21
-rw-r--r--llvm/lib/TableGen/TGLexer.cpp1
-rw-r--r--llvm/lib/TableGen/TGLexer.h2
-rw-r--r--llvm/lib/TableGen/TGParser.cpp94
-rw-r--r--llvm/lib/TableGen/TGParser.h1
-rw-r--r--llvm/test/TableGen/find.td64
8 files changed, 195 insertions, 10 deletions
diff --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst
index 8a9842f5b288..9b9ae58e110c 100644
--- a/llvm/docs/TableGen/ProgRef.rst
+++ b/llvm/docs/TableGen/ProgRef.rst
@@ -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*.
diff --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h
index d8f978f50447..717347ae03ba 100644
--- a/llvm/include/llvm/TableGen/Record.h
+++ b/llvm/include/llvm/TableGen/Record.h
@@ -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;
diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp
index 6e63fb3f7fff..fc80a2648f9c 100644
--- a/llvm/lib/TableGen/Record.cpp
+++ b/llvm/lib/TableGen/Record.cpp
@@ -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()) +
diff --git a/llvm/lib/TableGen/TGLexer.cpp b/llvm/lib/TableGen/TGLexer.cpp
index 60a0e34ea8e8..91229818077f 100644
--- a/llvm/lib/TableGen/TGLexer.cpp
+++ b/llvm/lib/TableGen/TGLexer.cpp
@@ -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);
diff --git a/llvm/lib/TableGen/TGLexer.h b/llvm/lib/TableGen/TGLexer.h
index 2f322f705e0d..857ba09782e8 100644
--- a/llvm/lib/TableGen/TGLexer.h
+++ b/llvm/lib/TableGen/TGLexer.h
@@ -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,
diff --git a/llvm/lib/TableGen/TGParser.cpp b/llvm/lib/TableGen/TGParser.cpp
index 07cf89b6062e..8d228d73f253 100644
--- a/llvm/lib/TableGen/TGParser.cpp
+++ b/llvm/lib/TableGen/TGParser.cpp
@@ -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);
}
}
diff --git a/llvm/lib/TableGen/TGParser.h b/llvm/lib/TableGen/TGParser.h
index f4311346555d..6e3c5186e4f6 100644
--- a/llvm/lib/TableGen/TGParser.h
+++ b/llvm/lib/TableGen/TGParser.h
@@ -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();
diff --git a/llvm/test/TableGen/find.td b/llvm/test/TableGen/find.td
new file mode 100644
index 000000000000..f5448dd8684d
--- /dev/null
+++ b/llvm/test/TableGen/find.td
@@ -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
+