Support parsing and testing unimplemented expressions (#957)

Co-authored-by: Jon Meow <46229924+jonmeow@users.noreply.github.com>
This commit is contained in:
Geoff Romer
2021-11-30 12:29:15 -08:00
committed by GitHub
co-authored by Jon Meow
parent be0c1e9da6
commit dc5e62fc7a
23 changed files with 463 additions and 147 deletions
+22
View File
@@ -22,6 +22,17 @@ cc_test(
],
)
cc_library(
name = "parse_test_matchers",
testonly = 1,
srcs = ["parse_test_matchers_internal.h"],
hdrs = ["parse_test_matchers.h"],
deps = [
":syntax",
"@com_google_googletest//:gtest",
],
)
cc_library(
name = "syntax",
srcs = [
@@ -108,3 +119,14 @@ mypy_test(
include_imports = True,
deps = [":format_grammar_lib"],
)
cc_test(
name = "unimplemented_example_test",
srcs = ["unimplemented_example_test.cpp"],
deps = [
":parse_test_matchers",
":syntax",
"//executable_semantics/ast:ast_test_matchers",
"@com_google_googletest//:gtest_main",
],
)
+2
View File
@@ -77,6 +77,7 @@ STRING "String"
TRUE "true"
TYPE "Type"
UNDERSCORE "_"
UNIMPL_EXAMPLE "__unimplemented_example_infix"
VAR "var"
WHILE "while"
/* table-end */
@@ -169,6 +170,7 @@ string_literal \"([^\\\"\n\v\f\r]|\\.)*\"
{TRUE} { return SIMPLE_TOKEN(TRUE); }
{TYPE} { return SIMPLE_TOKEN(TYPE); }
{UNDERSCORE} { return SIMPLE_TOKEN(UNDERSCORE); }
{UNIMPL_EXAMPLE} { return SIMPLE_TOKEN(UNIMPL_EXAMPLE); }
{VAR} { return SIMPLE_TOKEN(VAR); }
{WHILE} { return SIMPLE_TOKEN(WHILE); }
/* table-end */
@@ -0,0 +1,23 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef EXECUTABLE_SEMANTICS_SYNTAX_PARSE_TEST_MATCHERS_H_
#define EXECUTABLE_SEMANTICS_SYNTAX_PARSE_TEST_MATCHERS_H_
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "executable_semantics/syntax/parse_test_matchers_internal.h"
namespace Carbon {
// Matches the return value of `Parse()` if it represents a successful parse
// whose output matches the given matcher.
inline auto ParsedAs(::testing::Matcher<AST> ast_matcher) {
return TestingInternal::ParsedAsMatcher(std::move(ast_matcher));
}
} // namespace Carbon
#endif // EXECUTABLE_SEMANTICS_SYNTAX_PARSE_TEST_MATCHERS_H_
@@ -0,0 +1,59 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef EXECUTABLE_SEMANTICS_SYNTAX_PARSE_TEST_MATCHERS_INTERNAL_H_
#define EXECUTABLE_SEMANTICS_SYNTAX_PARSE_TEST_MATCHERS_INTERNAL_H_
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <ostream>
#include <variant>
#include "executable_semantics/syntax/parse.h"
namespace Carbon {
namespace TestingInternal {
// Implementation of ParsedAs(). See there for detailed documentation.
class ParsedAsMatcher {
public:
using is_gtest_matcher = void;
explicit ParsedAsMatcher(::testing::Matcher<AST> ast_matcher)
: ast_matcher_(std::move(ast_matcher)) {}
void DescribeTo(std::ostream* out) const {
DescribeToImpl(out, /*negated=*/false);
}
void DescribeNegationTo(std::ostream* out) const {
DescribeToImpl(out, /*negated=*/true);
}
auto MatchAndExplain(const std::variant<AST, SyntaxErrorCode>& result,
::testing::MatchResultListener* listener) const -> bool {
if (std::holds_alternative<SyntaxErrorCode>(result)) {
*listener << "holds error code " << std::get<SyntaxErrorCode>(result);
return false;
} else {
*listener << "is a successful parse whose ";
return ast_matcher_.MatchAndExplain(std::get<AST>(result), listener);
}
}
private:
void DescribeToImpl(std::ostream* out, bool negated) const {
*out << "is " << (negated ? "not " : "")
<< "a successful parse result whose ";
ast_matcher_.DescribeTo(out);
}
::testing::Matcher<AST> ast_matcher_;
};
} // namespace TestingInternal
} // namespace Carbon
#endif // EXECUTABLE_SEMANTICS_SYNTAX_PARSE_TEST_MATCHERS_INTERNAL_H_
+7
View File
@@ -190,6 +190,7 @@
TRUE
TYPE
UNDERSCORE
UNIMPL_EXAMPLE
VAR
WHILE
// table-end
@@ -223,6 +224,7 @@
// same precedence as POSTFIX_STAR.
%precedence POSTFIX_STAR UNARY_STAR
%left PERIOD ARROW
%nonassoc UNIMPL_EXAMPLE
%precedence
LEFT_PARENTHESIS
RIGHT_PARENTHESIS
@@ -381,6 +383,11 @@ expression:
}
| FN_TYPE tuple ARROW expression
{ $$ = arena->New<FunctionTypeLiteral>(context.source_loc(), $2, $4); }
| expression UNIMPL_EXAMPLE expression
{
$$ = arena->New<UnimplementedExpression>(context.source_loc(),
"ExampleInfix", $1, $3);
}
;
designator: PERIOD identifier { $$ = $2; }
;
@@ -0,0 +1,36 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "executable_semantics/ast/ast_test_matchers.h"
#include "executable_semantics/syntax/parse.h"
#include "executable_semantics/syntax/parse_test_matchers.h"
namespace Carbon {
namespace {
using ::testing::ElementsAre;
TEST(UnimplementedExampleTest, VerifyPrecedence) {
static constexpr std::string_view Program = R"(
package ExecutableSemanticsTest api;
fn Main() -> i32 {
return 1 __unimplemented_example_infix 2 + 3;
}
)";
Arena arena;
EXPECT_THAT(ParseFromString(&arena, "dummy.carbon", Program, false),
ParsedAs(ASTDeclarations(
ElementsAre(MatchesFunctionDeclaration().WithBody(
BlockContentsAre(ElementsAre(MatchesReturn(MatchesAdd(
MatchesUnimplementedExpression(
"ExampleInfix", ElementsAre(MatchesLiteral(1),
MatchesLiteral(2))),
MatchesLiteral(3))))))))));
}
} // namespace
} // namespace Carbon