Skip to content

Commit c022be9

Browse files
committed
Use a smaller executable grammar with the full grammar as fallback
1 parent d1cfd96 commit c022be9

File tree

4 files changed

+68
-16
lines changed

4 files changed

+68
-16
lines changed

include/graphqlservice/GraphQLGrammar.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,17 @@ struct document : must<document_content>
11451145
{
11461146
};
11471147

1148+
struct executable_document_content
1149+
: seq<bof, opt<utf8::bom>, star<ignored>, list<executable_definition, star<ignored>>,
1150+
star<ignored>, tao::graphqlpeg::eof>
1151+
{
1152+
};
1153+
1154+
// http://spec.graphql.org/June2018/#Document
1155+
struct executable_document : must<executable_document_content>
1156+
{
1157+
};
1158+
11481159
} /* namespace graphql::peg */
11491160

11501161
#endif // GRAPHQLGRAMMAR_H

samples/CMakeLists.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ if(GRAPHQL_UPDATE_SAMPLES)
1212
${CMAKE_CURRENT_BINARY_DIR}/unified/TodaySchema.cpp
1313
${CMAKE_CURRENT_BINARY_DIR}/unified/TodaySchema.h
1414
COMMAND schemagen --schema="${CMAKE_CURRENT_SOURCE_DIR}/schema.today.graphql" --prefix="Today" --namespace="today"
15-
DEPENDS schemagen schema.today.graphql
15+
DEPENDS schemagen graphqlpeg schema.today.graphql
1616
WORKING_DIRECTORY unified
1717
COMMENT "Generating mock TodaySchema files")
1818

@@ -24,7 +24,7 @@ if(GRAPHQL_UPDATE_SAMPLES)
2424
${CMAKE_CURRENT_BINARY_DIR}/unified_nointrospection/TodaySchema.cpp
2525
${CMAKE_CURRENT_BINARY_DIR}/unified_nointrospection/TodaySchema.h
2626
COMMAND schemagen --schema="${CMAKE_CURRENT_SOURCE_DIR}/schema.today.graphql" --prefix="Today" --namespace="today" --no-introspection
27-
DEPENDS schemagen schema.today.graphql
27+
DEPENDS schemagen graphqlpeg schema.today.graphql
2828
WORKING_DIRECTORY unified_nointrospection
2929
COMMENT "Generating mock TodaySchema files without Introspection (--no-introspection)")
3030

@@ -41,7 +41,7 @@ if(GRAPHQL_UPDATE_SAMPLES)
4141
add_custom_command(
4242
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/separate/today_schema_files
4343
COMMAND schemagen --schema="${CMAKE_CURRENT_SOURCE_DIR}/schema.today.graphql" --prefix="Today" --namespace="today" --separate-files > today_schema_files
44-
DEPENDS schemagen schema.today.graphql
44+
DEPENDS schemagen graphqlpeg schema.today.graphql
4545
WORKING_DIRECTORY separate
4646
COMMENT "Generating mock TodaySchema (--separate-files)")
4747

@@ -51,7 +51,7 @@ if(GRAPHQL_UPDATE_SAMPLES)
5151
add_custom_command(
5252
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/separate_nointrospection/today_schema_files
5353
COMMAND schemagen --schema="${CMAKE_CURRENT_SOURCE_DIR}/schema.today.graphql" --prefix="Today" --namespace="today" --no-introspection --separate-files > today_schema_files
54-
DEPENDS schemagen schema.today.graphql
54+
DEPENDS schemagen graphqlpeg schema.today.graphql
5555
WORKING_DIRECTORY separate_nointrospection
5656
COMMENT "Generating mock TodaySchema without Introspection (--no-introspection --separate-files)")
5757

@@ -68,7 +68,7 @@ if(GRAPHQL_UPDATE_SAMPLES)
6868
${CMAKE_CURRENT_BINARY_DIR}/validation/ValidationSchema.cpp
6969
${CMAKE_CURRENT_BINARY_DIR}/validation/ValidationSchema.h
7070
COMMAND schemagen --schema="${CMAKE_CURRENT_SOURCE_DIR}/schema.validation.graphql" --prefix="Validation" --namespace="validation"
71-
DEPENDS schemagen schema.validation.graphql
71+
DEPENDS schemagen graphqlpeg schema.validation.graphql
7272
WORKING_DIRECTORY validation
7373
COMMENT "Generating ValidationSchema files")
7474

src/GraphQLTree.cpp

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,13 @@ std::string_view ast_node::unescaped_view() const
6262
joined.append(child->string_view());
6363
}
6464

65-
const_cast<ast_node*>(this)->_unescaped = std::make_unique<unescaped_t>(std::move(joined));
65+
const_cast<ast_node*>(this)->_unescaped =
66+
std::make_unique<unescaped_t>(std::move(joined));
6667
}
6768
else if (!children.empty())
6869
{
69-
const_cast<ast_node*>(this)->_unescaped = std::make_unique<unescaped_t>(children.front()->string_view());
70+
const_cast<ast_node*>(this)->_unescaped =
71+
std::make_unique<unescaped_t>(children.front()->string_view());
7072
}
7173
else if (has_content() && is_type<escaped_unicode>())
7274
{
@@ -77,11 +79,13 @@ std::string_view ast_node::unescaped_view() const
7779
utf8.reserve((content.size() + 1) / 2);
7880
unescape::unescape_j::apply(in, utf8);
7981

80-
const_cast<ast_node*>(this)->_unescaped = std::make_unique<unescaped_t>(std::move(utf8));
82+
const_cast<ast_node*>(this)->_unescaped =
83+
std::make_unique<unescaped_t>(std::move(utf8));
8184
}
8285
else
8386
{
84-
const_cast<ast_node*>(this)->_unescaped = std::make_unique<unescaped_t>(std::string_view {});
87+
const_cast<ast_node*>(this)->_unescaped =
88+
std::make_unique<unescaped_t>(std::string_view {});
8589
}
8690
}
8791

@@ -720,6 +724,9 @@ const std::string ast_control<input_object_type_extension_content>::error_messag
720724
template <>
721725
const std::string ast_control<document_content>::error_message =
722726
"Expected http://spec.graphql.org/June2018/#Document";
727+
template <>
728+
const std::string ast_control<executable_document_content>::error_message =
729+
"Expected http://spec.graphql.org/June2018/#Document";
723730

724731
ast parseSchemaString(std::string_view input)
725732
{
@@ -756,8 +763,21 @@ ast parseString(std::string_view input)
756763
const auto& data = std::get<std::vector<char>>(result.input->data);
757764
memory_input<> in(data.data(), data.size(), "GraphQL");
758765

759-
result.root =
760-
parse_tree::parse<document, ast_node, executable_selector, nothing, ast_control>(std::move(in));
766+
try
767+
{
768+
// Try a smaller grammar with only executable definitions first.
769+
result.root = parse_tree::
770+
parse<executable_document, ast_node, executable_selector, nothing, ast_control>(
771+
std::move(in));
772+
}
773+
catch (const peg::parse_error&)
774+
{
775+
// Try again with the full document grammar so validation can handle the unexepected type
776+
// definitions if this is a mixed document.
777+
result.root =
778+
parse_tree::parse<document, ast_node, executable_selector, nothing, ast_control>(
779+
std::move(in));
780+
}
761781

762782
return result;
763783
}
@@ -769,8 +789,21 @@ ast parseFile(std::string_view filename)
769789
{} };
770790
auto& in = *std::get<std::unique_ptr<file_input<>>>(result.input->data);
771791

772-
result.root =
773-
parse_tree::parse<document, ast_node, executable_selector, nothing, ast_control>(std::move(in));
792+
try
793+
{
794+
// Try a smaller grammar with only executable definitions first.
795+
result.root = parse_tree::
796+
parse<executable_document, ast_node, executable_selector, nothing, ast_control>(
797+
std::move(in));
798+
}
799+
catch (const peg::parse_error&)
800+
{
801+
// Try again with the full document grammar so validation can handle the unexepected type
802+
// definitions if this is a mixed document.
803+
result.root =
804+
parse_tree::parse<document, ast_node, executable_selector, nothing, ast_control>(
805+
std::move(in));
806+
}
774807

775808
return result;
776809
}
@@ -783,9 +816,11 @@ peg::ast operator"" _graphql(const char* text, size_t size)
783816

784817
return { std::make_shared<peg::ast_input>(
785818
peg::ast_input { { std::string_view { text, size } } }),
786-
peg::parse_tree::
787-
parse<peg::document, peg::ast_node, peg::executable_selector, peg::nothing, peg::ast_control>(
788-
std::move(in)) };
819+
peg::parse_tree::parse<peg::document,
820+
peg::ast_node,
821+
peg::executable_selector,
822+
peg::nothing,
823+
peg::ast_control>(std::move(in)) };
789824
}
790825

791826
} /* namespace graphql */

test/PegtlTests.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,3 +413,9 @@ TEST(PegtlCase, AnalyzeGrammar)
413413
ASSERT_EQ(0, analyze<document>(true))
414414
<< "there shuldn't be any infinite loops in the PEG version of the grammar";
415415
}
416+
417+
TEST(PegtlCase, AnalyzeExecutableGrammar)
418+
{
419+
ASSERT_EQ(0, analyze<executable_document>(true))
420+
<< "there shuldn't be any infinite loops in the PEG version of the grammar";
421+
}

0 commit comments

Comments
 (0)