Add support for named values in the parser.

Summary:
Add support for named values in the parser.
This allows injection of arbitrary constants using a custom Sema object.
Completions are not supported right now.

Will be used by clang_query to support the 'let' command.
Usage example:
  clang_query> let unique_ptr recordDecl(hasName("unique_ptr"))
  clang_query> match varDecl(hasType(unique_ptr))

Reviewers: klimek, pcc

CC: cfe-commits, klimek

Differential Revision: http://llvm-reviews.chandlerc.com/D3229

llvm-svn: 205419
This commit is contained in:
Samuel Benzaquen 2014-04-02 13:11:45 +00:00
parent 11ff0a26a4
commit 2019cea863
5 changed files with 95 additions and 26 deletions

View File

@ -18,13 +18,14 @@
///
/// \code
/// Grammar for the expressions supported:
/// <Expression> := <Literal> | <MatcherExpression>
/// <Expression> := <Literal> | <NamedValue> | <MatcherExpression>
/// <Literal> := <StringLiteral> | <Unsigned>
/// <StringLiteral> := "quoted string"
/// <Unsigned> := [0-9]+
/// <MatcherExpression> := <MatcherName>(<ArgumentList>) |
/// <MatcherName>(<ArgumentList>).bind(<StringLiteral>)
/// <MatcherName> := [a-zA-Z]+
/// <NamedValue> := <Identifier>
/// <MatcherExpression> := <Identifier>(<ArgumentList>) |
/// <Identifier>(<ArgumentList>).bind(<StringLiteral>)
/// <Identifier> := [a-zA-Z]+
/// <ArgumentList> := <Expression> | <Expression>,<ArgumentList>
/// \endcode
///
@ -62,6 +63,19 @@ public:
public:
virtual ~Sema();
/// \brief Lookup a value by name.
///
/// This can be used in the Sema layer to declare known constants or to
/// allow to split an expression in pieces.
///
/// \param Name The name of the value to lookup.
///
/// \return The named value. It could be any type that VariantValue
/// supports. A 'nothing' value means that the name is not recognized.
virtual VariantValue getNamedValue(StringRef Name) {
return VariantValue();
}
/// \brief Process a matcher expression.
///
/// All the arguments passed here have already been processed.
@ -100,6 +114,21 @@ public:
Diagnostics *Error) = 0;
};
/// \brief Sema implementation that uses the matcher registry to process the
/// tokens.
class RegistrySema : public Parser::Sema {
public:
virtual ~RegistrySema();
llvm::Optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName,
const SourceRange &NameRange,
Diagnostics *Error) override;
VariantMatcher actOnMatcherExpression(MatcherCtor Ctor,
const SourceRange &NameRange,
StringRef BindID,
ArrayRef<ParserValue> Args,
Diagnostics *Error) override;
};
/// \brief Parse a matcher expression, creating matchers from the registry.
///
/// This overload creates matchers calling directly into the registry. If the

View File

@ -78,7 +78,8 @@ public:
/// \brief Clones the provided matchers.
///
/// They should be the result of a polymorphic matcher.
static VariantMatcher PolymorphicMatcher(std::vector<DynTypedMatcher> Matchers);
static VariantMatcher
PolymorphicMatcher(std::vector<DynTypedMatcher> Matchers);
/// \brief Creates a 'variadic' operator matcher.
///
@ -208,6 +209,9 @@ public:
VariantValue(const std::string &String);
VariantValue(const VariantMatcher &Matchers);
/// \brief Returns true iff this is an empty value.
bool isNothing() const { return Type == VT_Nothing; }
/// \brief Unsigned value functions.
bool isUnsigned() const;
unsigned getUnsigned() const;

View File

@ -424,8 +424,18 @@ bool Parser::parseExpressionImpl(VariantValue *Value) {
*Value = Tokenizer->consumeNextToken().Value;
return true;
case TokenInfo::TK_Ident:
case TokenInfo::TK_Ident: {
// Identifier could be a name known by Sema as a named value.
const VariantValue NamedValue =
S->getNamedValue(Tokenizer->peekNextToken().Text);
if (!NamedValue.isNothing()) {
Tokenizer->consumeNextToken(); // Actually consume it.
*Value = NamedValue;
return true;
}
// Fallback to full matcher parsing.
return parseMatcherExpressionImpl(Value);
}
case TokenInfo::TK_CodeCompletion:
addExpressionCompletions();
@ -457,27 +467,23 @@ Parser::Parser(CodeTokenizer *Tokenizer, Sema *S,
Diagnostics *Error)
: Tokenizer(Tokenizer), S(S), Error(Error) {}
class RegistrySema : public Parser::Sema {
public:
virtual ~RegistrySema() {}
llvm::Optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName,
const SourceRange &NameRange,
Diagnostics *Error) {
return Registry::lookupMatcherCtor(MatcherName, NameRange, Error);
Parser::RegistrySema::~RegistrySema() {}
llvm::Optional<MatcherCtor> Parser::RegistrySema::lookupMatcherCtor(
StringRef MatcherName, const SourceRange &NameRange, Diagnostics *Error) {
return Registry::lookupMatcherCtor(MatcherName, NameRange, Error);
}
VariantMatcher Parser::RegistrySema::actOnMatcherExpression(
MatcherCtor Ctor, const SourceRange &NameRange, StringRef BindID,
ArrayRef<ParserValue> Args, Diagnostics *Error) {
if (BindID.empty()) {
return Registry::constructMatcher(Ctor, NameRange, Args, Error);
} else {
return Registry::constructBoundMatcher(Ctor, NameRange, BindID, Args,
Error);
}
VariantMatcher actOnMatcherExpression(MatcherCtor Ctor,
const SourceRange &NameRange,
StringRef BindID,
ArrayRef<ParserValue> Args,
Diagnostics *Error) {
if (BindID.empty()) {
return Registry::constructMatcher(Ctor, NameRange, Args, Error);
} else {
return Registry::constructBoundMatcher(Ctor, NameRange, BindID, Args,
Error);
}
}
};
}
bool Parser::parseExpression(StringRef Code, VariantValue *Value,
Diagnostics *Error) {

View File

@ -175,6 +175,29 @@ TEST(ParserTest, FullParserTest) {
EXPECT_TRUE(matches("void f(int a, int x);", M));
EXPECT_FALSE(matches("void f(int x, int a);", M));
// Test named values.
struct NamedSema : public Parser::RegistrySema {
public:
virtual VariantValue getNamedValue(StringRef Name) {
if (Name == "nameX")
return std::string("x");
if (Name == "param0")
return VariantMatcher::SingleMatcher(hasParameter(0, hasName("a")));
return VariantValue();
}
};
NamedSema Sema;
llvm::Optional<DynTypedMatcher> HasParameterWithNamedValues(
Parser::parseMatcherExpression(
"functionDecl(param0, hasParameter(1, hasName(nameX)))", &Sema,
&Error));
EXPECT_EQ("", Error.toStringFull());
M = HasParameterWithNamedValues->unconditionalConvertTo<Decl>();
EXPECT_TRUE(matches("void f(int a, int x);", M));
EXPECT_FALSE(matches("void f(int x, int a);", M));
EXPECT_TRUE(!Parser::parseMatcherExpression(
"hasInitializer(\n binaryOperator(hasLHS(\"A\")))",
&Error).hasValue());

View File

@ -26,6 +26,7 @@ TEST(VariantValueTest, Unsigned) {
EXPECT_TRUE(Value.isUnsigned());
EXPECT_EQ(kUnsigned, Value.getUnsigned());
EXPECT_FALSE(Value.isNothing());
EXPECT_FALSE(Value.isString());
EXPECT_FALSE(Value.isMatcher());
}
@ -38,6 +39,7 @@ TEST(VariantValueTest, String) {
EXPECT_EQ(kString, Value.getString());
EXPECT_EQ("String", Value.getTypeAsString());
EXPECT_FALSE(Value.isNothing());
EXPECT_FALSE(Value.isUnsigned());
EXPECT_FALSE(Value.isMatcher());
}
@ -45,6 +47,7 @@ TEST(VariantValueTest, String) {
TEST(VariantValueTest, DynTypedMatcher) {
VariantValue Value = VariantMatcher::SingleMatcher(stmt());
EXPECT_FALSE(Value.isNothing());
EXPECT_FALSE(Value.isUnsigned());
EXPECT_FALSE(Value.isString());
@ -74,11 +77,13 @@ TEST(VariantValueTest, Assignment) {
VariantValue Value = std::string("A");
EXPECT_TRUE(Value.isString());
EXPECT_EQ("A", Value.getString());
EXPECT_FALSE(Value.isNothing());
EXPECT_FALSE(Value.isUnsigned());
EXPECT_FALSE(Value.isMatcher());
EXPECT_EQ("String", Value.getTypeAsString());
Value = VariantMatcher::SingleMatcher(recordDecl());
EXPECT_FALSE(Value.isNothing());
EXPECT_FALSE(Value.isUnsigned());
EXPECT_FALSE(Value.isString());
EXPECT_TRUE(Value.isMatcher());
@ -89,10 +94,12 @@ TEST(VariantValueTest, Assignment) {
Value = 17;
EXPECT_TRUE(Value.isUnsigned());
EXPECT_EQ(17U, Value.getUnsigned());
EXPECT_FALSE(Value.isNothing());
EXPECT_FALSE(Value.isMatcher());
EXPECT_FALSE(Value.isString());
Value = VariantValue();
EXPECT_TRUE(Value.isNothing());
EXPECT_FALSE(Value.isUnsigned());
EXPECT_FALSE(Value.isString());
EXPECT_FALSE(Value.isMatcher());