diff --git a/.github/workflows/test-cpp.yml b/.github/workflows/test-cpp.yml new file mode 100644 index 000000000..82ba366ec --- /dev/null +++ b/.github/workflows/test-cpp.yml @@ -0,0 +1,34 @@ +name: test-cpp + +on: + push: + branches: + - main + - renovate/** + pull_request: + branches: + - main + workflow_call: + +jobs: + test-cpp: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: install cmake and libraries + run: | + sudo apt-get update + sudo apt-get install ninja-build cmake + sudo apt-get install nlohmann-json3-dev + ninja --version + cmake --version + gcc --version + + - name: configure and build + env: + NPROCS: 1 + run: | + make acceptance + working-directory: cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index c0a008ae2..97be32227 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt - (i18n) Added Dutch translation of "Rule" - (i18n) Added Esperanto translation of "Rule" - [Ruby] Added `Gherkin::Query#parent_locations` for determining a scenario's parents' line numbers ([#89](https://github.com/cucumber/gherkin/pull/89)) +- C++ implementation [#117](https://github.com/cucumber/gherkin/pull/117) ## [26.2.0] - 2023-04-07 ### Changed diff --git a/README.md b/README.md index 21232345b..47fd6109f 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Gherkin is currently implemented for the following platforms (in order of birthd - [Perl](./perl) - Actively tested -> [workflow](./.github/workflows/test-perl.yml) - [PHP](./php) - Actively tested -> [workflow](./.github/workflows/test-php.yml) - [Dart](./dart) - Actively tested -> [workflow](./.github/workflows/test-dart.yml) +- [C++](./cpp) - Actively tested -> [workflow](./.github/workflows/test-cpp.yml) The CI will run using the linked workflow when that specific language implementation is changed diff --git a/cpp/.gitignore b/cpp/.gitignore new file mode 100644 index 000000000..5abb1e17c --- /dev/null +++ b/cpp/.gitignore @@ -0,0 +1,7 @@ +ext/ +build/ +acceptance/ +stage/ +.built +.configured +.deps-installed diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt new file mode 100644 index 000000000..85bc7d396 --- /dev/null +++ b/cpp/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) + +project(gherkin-cpp VERSION 1.0.0 LANGUAGES C CXX) + +include(GNUInstallDirs) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +find_package(nlohmann_json CONFIG REQUIRED) +find_package(cucumber-messages CONFIG REQUIRED) + +add_subdirectory(src/lib/gherkin) +add_subdirectory(src/bin/gherkin) +add_subdirectory(src/bin/gherkin-generate-tokens) + +install( + TARGETS + gherkin-cpp + gherkin-bin + gherkin-generate-tokens-bin + EXPORT gherkin-cpp-config + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +install( + DIRECTORY "${CMAKE_SOURCE_DIR}/include/" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gherkin +) + +install( + EXPORT gherkin-cpp-config + FILE gherkin-cpp-config.cmake + NAMESPACE gherkin-cpp:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/gherkin-cpp +) diff --git a/cpp/Makefile b/cpp/Makefile new file mode 100644 index 000000000..8bbb0c5ef --- /dev/null +++ b/cpp/Makefile @@ -0,0 +1,121 @@ +SHELL := /usr/bin/env bash + +GRAMMAR_GENERATED = \ + include/gherkin/rule_type.hpp \ + include/gherkin/parser.hpp +LANGUAGE_GENERATED = \ + src/lib/gherkin/dialect.cpp +ALL_GENERATED := $(GRAMMAR_GENERATED) $(LANGUAGE_GENERATED) +ALL_SOURCE_FILES = $(shell find include src -name "*.[ch]*") +SOURCE_FILES := $(filter-out $(ALL_GENERATED),$(ALL_SOURCE_FILES)) + +HERE = $(shell pwd) +CMAKE_BUILDROOT = $(HERE)/build/root +CMAKELISTS = $(shell find src -name CMakeLists.txt) + +GHERKIN = stage/bin/gherkin +GHERKIN_GENERATE_TOKENS = stage/bin/gherkin-generate-tokens + +GOOD_FEATURE_FILES = $(shell find ../testdata/good -name "*.feature") +BAD_FEATURE_FILES = $(shell find ../testdata/bad -name "*.feature") + +TOKENS = $(patsubst ../testdata/%,acceptance/testdata/%.tokens,$(GOOD_FEATURE_FILES)) +ASTS = $(patsubst ../testdata/%,acceptance/testdata/%.ast.ndjson,$(GOOD_FEATURE_FILES)) +PICKLES = $(patsubst ../testdata/%,acceptance/testdata/%.pickles.ndjson,$(GOOD_FEATURE_FILES)) +SOURCES = $(patsubst ../testdata/%,acceptance/testdata/%.source.ndjson,$(GOOD_FEATURE_FILES)) +ERRORS = $(patsubst ../testdata/%,acceptance/testdata/%.errors.ndjson,$(BAD_FEATURE_FILES)) + +.DEFAULT_GOAL = help + +help: ## Show this help + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \n\nWhere is one of:\n"} /^[$$()% a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +generate: $(GRAMMAR_GENERATED) ## Generate gherkin parser files + +generate-all: $(ALL_GENERATED) + +clean-generate: ## Remove generated Gherkin parser files ## Generate gherkin parser files + rm -f $(GRAMMAR_GENERATED) + +copy-gherkin-languages: + echo "Nothing to do" + +clean-gherkin-languages: ## Remove gherkin-languages.json and any derived files + echo "Nothing to do" + +clean: clean-deps ## Remove all build artifacts and files generated by the acceptance tests + rm -rf .built .configured + rm -rf acceptance + rm -rf build + +.DELETE_ON_ERROR: + +acceptance: .built $(TOKENS) $(ASTS) $(PICKLES) $(ERRORS) $(SOURCES) ## Build acceptance test dir and compare results with reference + +.built: .configured $(SOURCE_FILES) + cmake --build build/gherkin --parallel $(NPROCS) + cmake --install build/gherkin + touch $@ + +.configured: .deps-installed + rm -rf build/gherkin && mkdir -p build/gherkin + cmake \ + -G Ninja \ + -DCMAKE_PREFIX_PATH=$(CMAKE_BUILDROOT) \ + -DCMAKE_INSTALL_PREFIX=$(HERE)/stage \ + -S . \ + -B build/gherkin \ + --toolchain cmake/toolchains/ext.cmake + touch $@ + +define berp-generate-parser = +berp -g ../gherkin.berp -t $< -o $@ --noBOM +endef + +include/gherkin/rule_type.hpp: gherkin-cpp-rule-type.razor ../gherkin.berp + $(berp-generate-parser) + +include/gherkin/parser.hpp: gherkin-cpp-parser.razor ../gherkin.berp + $(berp-generate-parser) + +src/lib/gherkin/dialect.cpp: ../gherkin-languages.json dialect.cpp.jq + jq -f dialect.cpp.jq -r -c < $< > $@ + +acceptance/testdata/%.tokens: ../testdata/% ../testdata/%.tokens + mkdir -p $(@D) + $(GHERKIN_GENERATE_TOKENS) $< > $@ + diff --unified $<.tokens $@ + +acceptance/testdata/%.ast.ndjson: ../testdata/% ../testdata/%.ast.ndjson + mkdir -p $(@D) + $(GHERKIN) --no-source --no-pickles --predictable-ids $< | jq --sort-keys --compact-output "." > $@ + diff --unified <(jq "." $<.ast.ndjson) <(jq "." $@) + +acceptance/testdata/%.pickles.ndjson: ../testdata/% ../testdata/%.pickles.ndjson + mkdir -p $(@D) + $(GHERKIN) --no-source --no-ast --predictable-ids $< | jq --sort-keys --compact-output "." > $@ + diff --unified <(jq "." $<.pickles.ndjson) <(jq "." $@) + +acceptance/testdata/%.source.ndjson: ../testdata/% ../testdata/%.source.ndjson + mkdir -p $(@D) + $(GHERKIN) --no-ast --no-pickles --predictable-ids $< | jq --sort-keys --compact-output "." > $@ + diff --unified <(jq "." $<.source.ndjson) <(jq "." $@) + +acceptance/testdata/%.errors.ndjson: ../testdata/% ../testdata/%.errors.ndjson + mkdir -p $(@D) + $(GHERKIN) --no-source --predictable-ids $< | jq --sort-keys --compact-output "." > $@ + diff --unified <(jq "." $<.errors.ndjson) <(jq "." $@) + +# +# External dependencies +# +install-deps: .deps-installed +.PHONY: install-deps + +.deps-installed: + ./scripts/build-externals deps.txt + touch $@ + +clean-deps: + rm -rf .deps-installed build/root build/ext +.PHONY: clean-deps diff --git a/cpp/README.md b/cpp/README.md new file mode 100644 index 000000000..8a8f521f9 --- /dev/null +++ b/cpp/README.md @@ -0,0 +1,13 @@ +# Gherkin for C++ + +Gherkin parser/compiler for C++. Please see [Gherkin](https://github.com/cucumber/gherkin) for details. + +## Developers + +Some files are generated from the `gherkin-*.razor` file. Please run the +following command to generate the C++ files. + +~~~bash +cd /cpp +make generate-all +~~~ diff --git a/cpp/cmake/toolchains/ext.cmake b/cpp/cmake/toolchains/ext.cmake new file mode 100644 index 000000000..192b99b97 --- /dev/null +++ b/cpp/cmake/toolchains/ext.cmake @@ -0,0 +1,18 @@ +# +# CMake toolchain to be used when building external libraries +# + +# use, i.e. don't skip the full RPATH for the build tree +set(CMAKE_SKIP_BUILD_RPATH FALSE) + +find_program(CCACHE ccache) + +if(CCACHE) + set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE}") + set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE}") +endif() + +if(DEFINED ENV{GHERKIN_DEBUG}) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fsanitize=address") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fsanitize=address") +endif() diff --git a/cpp/deps.txt b/cpp/deps.txt new file mode 100644 index 000000000..b535c7d5a --- /dev/null +++ b/cpp/deps.txt @@ -0,0 +1,2 @@ +https://github.com/nlohmann/json/archive/refs/tags/v3.11.2.zip -DJSON_BuildTests=OFF +https://github.com/cucumber/messages/archive/refs/heads/main.zip --src-dir=cpp diff --git a/cpp/dialect.cpp.jq b/cpp/dialect.cpp.jq new file mode 100644 index 000000000..2affeac59 --- /dev/null +++ b/cpp/dialect.cpp.jq @@ -0,0 +1,44 @@ +. as $root | +[ + "#include \n\n", + "namespace gherkin {\n\n", + "const keywords_maps&\n", + "all_keywords()\n", + "{\n", + " static const keywords_maps kwms = {\n", + " ", + ( + [ + to_entries[] | .key as $lang | .value | + [ + ("{\n \"",$lang,"\",\n {\n"), + (" "), + ( + [ + { + "and", "background", "but", "examples", "feature", "given", + "rule", "scenario", "scenarioOutline", "then", "when" + } | to_entries[] | + [ + "{ \"", .key, "\", { ", + ( + [.value[] | [@json] | add] | join(", ") + ), + " } }" + ] | add + ] | join(",\n ") + ), + ("\n }\n }") + ] | add + ] | join(",\n ") + ), + "\n };\n\n", + " return kwms;", + "}\n\n", + "const keywords_map&\n", + "keywords(const std::string_view& language)\n", + "{\n", + " return all_keywords().at(language);\n", + "}\n\n", + "}\n" +] | add diff --git a/cpp/gherkin-cpp-parser.razor b/cpp/gherkin-cpp-parser.razor new file mode 100644 index 000000000..b53fa4aad --- /dev/null +++ b/cpp/gherkin-cpp-parser.razor @@ -0,0 +1,271 @@ +@using Berp; +@functions { +public static string ToSnakeCase(string str) +{ + return + string.Concat( + str.Select( + (x, i) => i > 0 && char.IsUpper(x) + ? "_" + x + : x.ToString() + ) + ).ToLower(); +} + +public static string NameOf(Rule rule) +{ return ToSnakeCase(rule.Name.Replace("#", "")); } +} +@helper CallProduction(ProductionRule production) +{ + var rname = ToSnakeCase(production.RuleName); + + switch(production.Type) + { + case ProductionRuleType.Start: + @: start_rule(context, rule_type::@rname); + break; + case ProductionRuleType.End: + @: end_rule(context, rule_type::@rname); + break; + case ProductionRuleType.Process: + @: build(context, token); + break; + } +} +@helper HandleParserError(IEnumerable expectedTokens, State state) +{ + std::string state_comment = "State: @state.Id - @Raw(state.Comment)"; + std::string expected_tokens = "@Raw(string.Join(", ", expectedTokens))"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return @state.Id; +} +@helper MatchToken(TokenType tokenType) +{match_@(ToSnakeCase(tokenType.Name))(context, token)} +// This file is generated. Do not edit! Edit gherkin-cpp-parser.razor instead. +#include +#include + +namespace gherkin { + +template < + typename Builder = ast_builder, + typename Scanner = token_scanner, + typename Matcher = token_matcher +> +class parser : public parser_base +{ +public: + using parent = parser_base; + using parent::parent; + using parent::parse; + using context_type = typename parent::context_type; + +protected: + void parse(context_type& context) override + { + start_rule(context, rule_type::@NameOf(Model.RuleSet.StartRule)); + + std::size_t state = 0; + + while (true) { + auto token = context.read_token(); + state = match_token(state, token, context); + + if (token.is_eof()) { + break; + } + } + + end_rule(context, rule_type::@NameOf(Model.RuleSet.StartRule)); + + if (context.has_errors()) { + throw composite_parser_error(context.eptrs); + } + } + + void build(context_type& context, token& token) + { context.builder.build(token); } + + void start_rule(context_type& context, rule_type rule_type) + { + handle_ast_error( + context, + rule_type, + [&context](auto rtype) { + context.builder.start_rule(rtype); + } + ); + } + + void end_rule(context_type& context, rule_type rule_type) + { + handle_ast_error( + context, + rule_type, + [&context](auto rtype) { + context.builder.end_rule(rtype); + } + ); + } + + template + bool handle_external_error( + context_type& context, + bool default_value, + Argument&& argument, + Action&& action + ) + { + using ret_type = decltype(action(argument)); + + try { + if constexpr (std::is_same_v) { + action(argument); + return default_value; + } else { + return action(argument); + } + } catch (const composite_parser_error& e) { + for (const auto& ep : e.errors()) { + context.add_error(ep); + } + } catch (const parser_error& e) { + auto ep = new_parser_error(e); + context.add_error(ep); + } + + return default_value; + } + + template + void handle_ast_error( + context_type& context, + Argument&& argument, + Action&& action + ) + { handle_external_error(context, true, argument, action); } + +@foreach(var rule in Model.RuleSet.TokenRules) +{ + + bool match_@(NameOf(rule))(context_type& context, token& token) + { + @if (rule.Name != "#EOF") + { + @:if (token.is_eof()) { + @: return false; + @:} + @: + } + return + handle_external_error( + context, + false, + token, + [&context](auto& t) { + return context.matcher.match_@(NameOf(rule))(t); + } + ); + } + +} + +@foreach(var lookAheadHint in Model.RuleSet.LookAheadHints) +{ + + bool lookahead_@(lookAheadHint.Id)(context_type& context, token& current_token) + { + current_token.detach(); + token token; + token_queue queue; + bool match = false; + + while (true) { + token = context.read_token(); + token.detach(); + queue.push_back(token); + + if (@foreach(var tokenType in lookAheadHint.ExpectedTokens) {@MatchToken(tokenType) || }false) { + match = true; + break; + } + + if (!(@foreach(var tokenType in lookAheadHint.Skip) {@MatchToken(tokenType) || }false)) { + break; + } + } + + context.push_tokens(queue); + + return match; + } + +} + +@foreach(var state in Model.States.Values.Where(s => !s.IsEndState)) +{ + // @Raw(state.Comment) + std::size_t match_token_at_@(state.Id)(token& token, context_type& context) + { +@{var indent = " ";} + @foreach(var transition in state.Transitions) + { + @:if (@MatchToken(transition.TokenType)) { + if (transition.LookAheadHint != null) + { + @:if (lookahead_@(transition.LookAheadHint.Id)(context, token)) { + indent = " "; + } + foreach(var production in transition.Productions) + { + @indent@CallProduction(production) + } + @:@(indent)return @transition.TargetState; + if (transition.LookAheadHint != null) + { + indent = " "; + @:} + } + @:} + } + @HandleParserError(state.Transitions.Select(t => "#" + t.TokenType.ToString()).Distinct(), state) + } + +} + + std::size_t match_token(std::size_t state, token& token, context_type& context) + { + switch (state) { + @foreach(var state in Model.States.Values.Where(s => !s.IsEndState)) + { + @:case @state.Id: + @:return match_token_at_@(state.Id)(token, context); + } + default: + throw + std::runtime_error( + "invalid operation: " + std::to_string(state) + ); + + return -1; + } + } +}; + +} diff --git a/cpp/gherkin-cpp-rule-type.razor b/cpp/gherkin-cpp-rule-type.razor new file mode 100644 index 000000000..648631543 --- /dev/null +++ b/cpp/gherkin-cpp-rule-type.razor @@ -0,0 +1,48 @@ +// This file is generated. Do not edit! Edit gherkin-cpp-rule-type.razor instead. +@using Berp; +@functions { +public static string ToSnakeCase(string str) +{ + return + string.Concat( + str.Select( + (x, i) => i > 0 && char.IsUpper(x) + ? "_" + x + : x.ToString() + ) + ).ToLower(); +} + +public static string NameOf(Rule rule) +{ return ToSnakeCase(rule.Name.Replace("#", "")); } + +} +#pragma once + +#include +#include + +namespace gherkin { + +enum class rule_type +{ + none = 0, + @foreach(var rule in Model.RuleSet.Where(r => !r.TempRule)) + { @NameOf(rule), +} + count +}; + +std::string_view +to_string(rule_type r); + +inline +std::ostream& +operator<<(std::ostream& os, rule_type r) +{ + os << to_string(r); + + return os; +} + +} diff --git a/cpp/include/gherkin/app.hpp b/cpp/include/gherkin/app.hpp new file mode 100644 index 000000000..a9f254936 --- /dev/null +++ b/cpp/include/gherkin/app.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include +#include +#include +#include + +namespace gherkin { + +class app +{ +public: + using parser = gherkin::parser<>; + using parser_result = typename parser::result_type; + using callbacks = gherkin::callbacks; + + app(); + virtual ~app(); + + void include_source(bool f); + void include_ast(bool f); + void include_pickles(bool f); + + void parse(const file& f, const callbacks& cbs = {}); + void parse(const cms::envelope& e, const callbacks& cbs = {}); + void parse(const cms::source& s, const callbacks& cbs = {}); + +private: + void send_parse_error( + const std::string& uri, + const parser_error& e, + const callbacks& cbs + ) const; + + id_generator_ptr idp_; + parser p_; + bool include_source_ = true; + bool include_ast_ = true; + bool include_pickles_ = true; +}; + +} diff --git a/cpp/include/gherkin/ast_builder.hpp b/cpp/include/gherkin/ast_builder.hpp new file mode 100644 index 000000000..a63dabeac --- /dev/null +++ b/cpp/include/gherkin/ast_builder.hpp @@ -0,0 +1,89 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace gherkin { + +namespace cms = cucumber::messages; + +using table_rows = std::vector; +using table_cells = std::vector; +using tags = std::vector; +using comments = std::vector; + +class ast_builder +{ +public: + using result_type = cms::gherkin_document; + + ast_builder(); + ast_builder(id_generator_ptr idp); + + virtual ~ast_builder(); + + void reset(std::string_view uri); + + void start_rule(rule_type rule_type); + void end_rule(rule_type rule_type); + void build(const token& token); + + const cms::gherkin_document& get_result() const; + +private: + using ast_node_stack = std::stack; + + std::string next_id(); + + void transform_node(ast_node& from, ast_node& to); + + cms::step make_step(ast_node& node); + cms::doc_string make_doc_string(ast_node& node); + cms::data_table make_data_table(ast_node& node); + cms::background make_background(ast_node& node); + cms::scenario make_scenario_definition(ast_node& node); + cms::examples make_examples_definition(ast_node& node); + table_rows make_examples_table(ast_node& node); + std::string make_description(ast_node& node); + cms::feature make_feature(ast_node& node); + cms::rule make_rule(ast_node& node); + cms::gherkin_document make_gherkin_document(ast_node& node); + + cms::location get_location( + const token& token, + std::size_t column = 0 + ) const; + + table_rows get_table_rows(const ast_node& node); + void ensure_cell_count(const table_rows& rows) const; + table_cells get_table_cells(const token& token); + tags get_tags(const ast_node& node); + + ast_node pop_node(); + ast_node& current_node(); + const ast_node& current_node() const; + + id_generator_ptr idp_; + ast_node_stack stack_; + std::string_view uri_; + comments comments_; + cms::gherkin_document doc_; +}; + +using ast_builder_ptr = std::unique_ptr; + +template +ast_builder_ptr +new_ast_builder(Args&&... args) +{ return std::make_unique(std::forward(args)...); } + +} diff --git a/cpp/include/gherkin/ast_node.hpp b/cpp/include/gherkin/ast_node.hpp new file mode 100644 index 000000000..eb282c30a --- /dev/null +++ b/cpp/include/gherkin/ast_node.hpp @@ -0,0 +1,168 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace gherkin { + +template +struct sub_node +{ + using type = std::decay_t; + using vector_type = std::vector; + using ptr_type = std::shared_ptr; + + sub_node(std::any& sub_item) + : ref_(sub_item) + { + if (!ref_.has_value()) { + ref_ = make(); + } + } + + static + auto make() + { return std::make_shared(); } + + auto& cast() + { return std::any_cast(ref_); } + + auto get_ptr() + { return cast().get(); } + + auto& get() + { return *cast(); } + + void emplace_back(const T& v) + { get().emplace_back(v); } + + std::any& ref_; +}; + +class ast_node +{ +public: + ast_node(rule_type rule_type = rule_type::none); + ast_node(const ast_node& other); + ast_node(ast_node&& other); + + virtual ~ast_node(); + + ast_node& operator=(const ast_node& other); + ast_node& operator=(ast_node&& other); + + bool is(rule_type rule_type) const; + + rule_type type() const; + + template + void add(rule_type rule_type, const T& v) + { + sub_node sn(sub_items_[rule_type]); + + sn.emplace_back(v); + } + + template + auto get_items( + rule_type rule_type, + const std::vector* default_result = nullptr + ) const + { + using stype = sub_node; + using ret_type = const typename stype::vector_type*; + + ret_type ret = default_result; + + auto it = sub_items_.find(rule_type); + + if (it != sub_items_.end()) { + stype sn(const_cast(it->second)); + + ret = sn.get_ptr(); + } + + return ret; + } + + template + const T* get_single( + rule_type rule_type, + const T* default_result = nullptr + ) const + { + auto items = get_items(rule_type); + + if (items && !items->empty()) { + return std::addressof(items->at(0)); + } + + return default_result; + } + + const auto& get_tokens(rule_type rule_type) const + { return *get_items(rule_type, &empty_tokens_); } + + const auto& get_token(rule_type rule_type) const + { return *get_single(rule_type, &null_token_); } + + template + void set_value(rule_type rule_type, V& v) const + { + using type = std::decay_t; + + if constexpr (is_container_v) { + using value_type = typename type::value_type; + using vector_type = std::vector; + + auto opt_items = get_items(rule_type); + + if (opt_items) { + auto& items = *opt_items; + + for (const auto& i : items) { + v.emplace_back(i); + } + } + } else { + auto pitem = get_single(rule_type); + + if (pitem) { + v = *pitem; + } + } + } + + template + void set(rule_type rule_type, T& v) const + { + using type = std::decay_t; + + if constexpr (is_container_v) { + using value_type = typename type::value_type; + + set_value(rule_type, v); + } else { + set_value(rule_type, v); + } + } + +private: + using sub_items = std::unordered_map; + + rule_type rule_type_; + sub_items sub_items_; + token null_token_; + std::vector empty_tokens_; +}; + +} diff --git a/cpp/include/gherkin/builder.hpp b/cpp/include/gherkin/builder.hpp new file mode 100644 index 000000000..cba657c00 --- /dev/null +++ b/cpp/include/gherkin/builder.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +#include +#include + +namespace gherkin { + +template +struct builder +{ + using result_type = T; + + virtual void reset(const std::string& uri = "") + {} + + virtual void start_rule(rule_type rule_type) + {} + + virtual void end_rule(rule_type rule_type) + {} + + virtual void build(const token& token) + {} + + result_type get_result() const + { return {}; } +}; + +template +using builder_ptr = std::unique_ptr>; + +template < + typename Builder, + typename T, + typename... Args +> +builder_ptr +new_builder(Args&&... args) +{ return std::make_unique(std::forward(args)...); } + +} diff --git a/cpp/include/gherkin/cb_types.hpp b/cpp/include/gherkin/cb_types.hpp new file mode 100644 index 000000000..ec53e2af0 --- /dev/null +++ b/cpp/include/gherkin/cb_types.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include + +#include +#include + +#include + +namespace gherkin { + +namespace cms = cucumber::messages; + +using source_cb = std::function; +using pickle_cb = std::function; +using error_cb = std::function; + +template +struct callbacks +{ + using ast_cb = std::function; + + gherkin::source_cb source; + ast_cb ast; + gherkin::pickle_cb pickle; + error_cb error; +}; + +template +void +call_cb(Callback& cb, const Msg& m) +{ + if (cb) { + cb(m); + } +} + +} diff --git a/cpp/include/gherkin/container_helpers.hpp b/cpp/include/gherkin/container_helpers.hpp new file mode 100644 index 000000000..feceb9dd9 --- /dev/null +++ b/cpp/include/gherkin/container_helpers.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +namespace gherkin { + +template +struct is_vector : public std::false_type{}; + +template +struct is_vector> : public std::true_type{}; + +template +using is_vector_t = typename is_vector::type; + +template +constexpr bool is_vector_v = is_vector::value; + +template +struct is_set : public std::false_type{}; + +template +struct is_set> : public std::true_type{}; + +template +using is_set_t = typename is_set::type; + +template +constexpr bool is_set_v = is_set::value; + +template +struct is_unordered_set : public std::false_type{}; + +template +struct is_unordered_set> : public std::true_type{}; + +template +using is_unordered_set_t = typename is_unordered_set::type; + +template +constexpr bool is_unordered_set_v = is_unordered_set::value; + +} diff --git a/cpp/include/gherkin/demangle.hpp b/cpp/include/gherkin/demangle.hpp new file mode 100644 index 000000000..d6f2b2f6c --- /dev/null +++ b/cpp/include/gherkin/demangle.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +namespace gherkin { + +namespace detail { + +auto +demangle(std::string&& name) +-> std::unique_ptr; + +} + +template +auto declname() +{ return detail::demangle(typeid(T).name()); } + +template +auto declname(T&& v) +{ return declname(); } + +} diff --git a/cpp/include/gherkin/dialect.hpp b/cpp/include/gherkin/dialect.hpp new file mode 100644 index 000000000..b7d59d4aa --- /dev/null +++ b/cpp/include/gherkin/dialect.hpp @@ -0,0 +1,31 @@ + #pragma once + +#include + +namespace gherkin { + +using keywords_map = std::unordered_map; +using keywords_maps = std::unordered_map; + +struct dialect +{ + const string_views& feature_keywords; + const string_views& rule_keywords; + const string_views& scenario_keywords; + const string_views& scenario_outline_keywords; + const string_views& background_keywords; + const string_views& examples_keywords; + const string_views& given_keywords; + const string_views& when_keywords; + const string_views& then_keywords; + const string_views& and_keywords; + const string_views& but_keywords; +}; + +const keywords_maps& +all_keywords(); + +const keywords_map& +keywords(const std::string_view& language); + +} diff --git a/cpp/include/gherkin/exceptions.hpp b/cpp/include/gherkin/exceptions.hpp new file mode 100644 index 000000000..770547936 --- /dev/null +++ b/cpp/include/gherkin/exceptions.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +namespace gherkin { + +namespace cms = cucumber::messages; + +class parser_error : public std::runtime_error +{ +public: + parser_error( + const std::string& message, + const cms::location& location + ); + + parser_error(const parser_error& other); + + virtual ~parser_error(); + + std::string make_message( + const std::string& message, + const cms::location& location + ) const; + + bool same_message(const parser_error& other) const; + + const cms::location& location() const; + +private: + cms::location location_; +}; + +using parser_error_ptr = std::shared_ptr; +using parser_error_ptrs = std::vector; + +template +parser_error_ptr +new_parser_error(Args&&... args) +{ return std::make_shared(std::forward(args)...); } + +using ast_builder_error = parser_error; + +class no_such_language_error : public parser_error +{ +public: + no_such_language_error( + const std::string& language, + const cms::location& location + ); + + virtual ~no_such_language_error(); +}; + +class unexpected_token : public parser_error +{ +public: + unexpected_token( + const token& received_token, + const std::string& expected_tokens, + const std::string& state_comment + ); + + virtual ~unexpected_token(); + + std::string make_message( + const token& received_token, + const std::string& expected_tokens + ) const; + + cms::location make_location(const token& t) const; + +private: + token received_token_; + std::string expected_tokens_; + std::string state_comment_; +}; + +class unexpected_eof : public parser_error +{ +public: + unexpected_eof( + const token& received_token, + const std::string& expected_tokens, + const std::string& state_comment + ); + + virtual ~unexpected_eof(); + + std::string make_message(const std::string& expected_tokens) const; + +private: + std::string expected_tokens_; + std::string state_comment_; +}; + +class composite_parser_error : public parser_error +{ +public: + composite_parser_error(const parser_error_ptrs& ptrs); + + virtual ~composite_parser_error(); + + std::string make_message(const parser_error_ptrs& ptrs) const; + + const parser_error_ptrs& errors() const; + +private: + parser_error_ptrs ptrs_; +}; + +} diff --git a/cpp/include/gherkin/file.hpp b/cpp/include/gherkin/file.hpp new file mode 100644 index 000000000..e616e0c6c --- /dev/null +++ b/cpp/include/gherkin/file.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace gherkin { + +struct file +{ + std::string path; +}; + +} diff --git a/cpp/include/gherkin/id_generator.hpp b/cpp/include/gherkin/id_generator.hpp new file mode 100644 index 000000000..5e7941d55 --- /dev/null +++ b/cpp/include/gherkin/id_generator.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +namespace gherkin { + +class id_generator_base +{ +public: + id_generator_base(); + + virtual ~id_generator_base(); + + virtual std::string next_id() = 0; +}; + +class id_generator : public id_generator_base +{ +public: + id_generator(); + virtual ~id_generator(); + + std::string next_id() override; + +private: + std::size_t id_counter_ = 0; +}; + +using id_generator_ptr = std::shared_ptr; + +template +id_generator_ptr +new_id_generator(Args&&... args) +{ return std::make_shared(std::forward(args)...); } + +} diff --git a/cpp/include/gherkin/items.hpp b/cpp/include/gherkin/items.hpp new file mode 100644 index 000000000..8b01b5144 --- /dev/null +++ b/cpp/include/gherkin/items.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace gherkin { + +struct item +{ + std::size_t column; + std::string text; +}; + +using items = std::vector; + +} diff --git a/cpp/include/gherkin/join_utils.hpp b/cpp/include/gherkin/join_utils.hpp new file mode 100644 index 000000000..fdc4cca96 --- /dev/null +++ b/cpp/include/gherkin/join_utils.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include +#include + +#include + +namespace gherkin { + +namespace detail { + +template +std::size_t +join_container_impl( + std::ostream& os, + const std::string& sep, + const Container& c +) +{ + if (!c.empty()) { + auto it = c.begin(); + + os << *it; + + while (++it != c.end()) { + os << sep << *it; + } + } + + return c.size(); +} + +} // namespace detail + +template +struct is_joinable_container +{ + using type = std::conditional_t< + is_vector_v + || is_set_v + || is_unordered_set_v, + std::true_type, + std::false_type + >; + + static constexpr typename type::value_type value = type::value; +}; + +template +using is_joinable_container_t = typename is_joinable_container::type; + +template +constexpr bool is_joinable_container_v = is_joinable_container::value; + +template +void +join_item_impl( + std::ostream& os, + const std::string& sep, + Args&&... args +) +{ + std::size_t prev_count = 0; + + auto join_arg = [&](auto&& arg) { + using type = std::decay_t; + + if constexpr (is_joinable_container_v) { + if (prev_count && !arg.empty()) { + os << sep; + } + + prev_count += detail::join_container_impl(os, sep, arg); + } else { + if (prev_count) { + os << sep; + } + + os << arg; + ++prev_count; + } + }; + + (join_arg(std::forward(args)), ...); +} + +template +std::string +join(const std::string& sep, Args&&... args) +{ + std::ostringstream oss; + + join_item_impl(oss, sep, std::forward(args)...); + + return oss.str(); +} + +} diff --git a/cpp/include/gherkin/keywords.hpp b/cpp/include/gherkin/keywords.hpp new file mode 100644 index 000000000..13a011d32 --- /dev/null +++ b/cpp/include/gherkin/keywords.hpp @@ -0,0 +1,14 @@ +#include + +namespace gherkin { + +const string_views& +keywords(const std::string_view& language, const std::string_view& kw); + +string_views +keywords(const std::string_view& language, const string_views& kws); + +dialect +get_dialect(const std::string_view& language); + +} diff --git a/cpp/include/gherkin/line.hpp b/cpp/include/gherkin/line.hpp new file mode 100644 index 000000000..727129b63 --- /dev/null +++ b/cpp/include/gherkin/line.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include +#include + +namespace gherkin { + +class line +{ +public: + line(); + line(const std::string& line_text, std::size_t line_number); + + std::string get_rest_trimmed(std::size_t length) const; + std::string get_keyword_trimmed(std::string_view kw) const; + + std::string_view get_line_text( + std::size_t indent_to_remove = std::string::npos + ) const; + + std::string_view line_text() const; + + std::size_t indent() const; + + bool is_empty() const; + + bool startswith(std::string_view prefix) const; + bool startswith_title_keyword(const std::string& keyword) const; + + items table_cells() const; + + items tags() const; + +private: + std::string line_text_; + std::size_t line_number_ = 0; + std::size_t indent_ = 0; + std::string trimmed_line_text_; +}; + +} diff --git a/cpp/include/gherkin/log.hpp b/cpp/include/gherkin/log.hpp new file mode 100644 index 000000000..fa0a0670b --- /dev/null +++ b/cpp/include/gherkin/log.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include + +namespace gherkin { + +template +std::string +log_string(Args&&... args) +{ + std::ostringstream oss; + + (oss << ... << args); + + return oss.str(); +} + +template +void +log(Args&&... args) +{ std::cout << log_string(std::forward(args)...) << std::endl; } + +template +void +warn(Args&&... args) +{ log("W: ", std::forward(args)...); } + +template +void +error(Args&&... args) +{ log("E: ", std::forward(args)...); } + +template +void +die(Args&&... args) +{ throw std::runtime_error(log_string(std::forward(args)...)); } + +template +void +die_if(bool cond, Args&&... args) +{ + if (!cond) { + return; + } + + die(std::forward(args)...); +} + +template +void +die_unless(bool cond, Args&&... args) +{ die_if(!cond, std::forward(args)...); } + +template +void +sysdie(Args&&... args) +{ + auto err = errno; + auto econd = std::system_category().default_error_condition(err); + + die(std::forward(args)..., ": ", econd.message()); +} + +template +void +sysdie_if(bool cond, Args&&... args) +{ + if (!cond) { + return; + } + + sysdie(std::forward(args)...); +} + +template +void +sysdie_unless(bool cond, Args&&... args) +{ sysdie_if(!cond, std::forward(args)...); } + +} diff --git a/cpp/include/gherkin/msg_types.hpp b/cpp/include/gherkin/msg_types.hpp new file mode 100644 index 000000000..0e755ce3b --- /dev/null +++ b/cpp/include/gherkin/msg_types.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include + +namespace gherkin { + +namespace cms = cucumber::messages; + +using pickles = std::vector; +using pickle_steps = std::vector; +using pickle_tags = std::vector; +using pickle_table_cells = std::vector; +using steps = std::vector; +using tags = std::vector; +using table_cells = std::vector; +using envelopes = std::vector; + +} diff --git a/cpp/include/gherkin/parse_error.hpp b/cpp/include/gherkin/parse_error.hpp new file mode 100644 index 000000000..aaddf2605 --- /dev/null +++ b/cpp/include/gherkin/parse_error.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include + +#include + +namespace gherkin { + +using json = nlohmann::json; +namespace cms = cucumber::messages; + +struct parse_error +{ + std::string uri; + cms::location location; + std::string message; + + json to_json() const; +}; + +} diff --git a/cpp/include/gherkin/parser.hpp b/cpp/include/gherkin/parser.hpp new file mode 100644 index 000000000..127a96483 --- /dev/null +++ b/cpp/include/gherkin/parser.hpp @@ -0,0 +1,4763 @@ +// This file is generated. Do not edit! Edit gherkin-cpp-parser.razor instead. +#include +#include + +namespace gherkin { + +template < + typename Builder = ast_builder, + typename Scanner = token_scanner, + typename Matcher = token_matcher +> +class parser : public parser_base +{ +public: + using parent = parser_base; + using parent::parent; + using parent::parse; + using context_type = typename parent::context_type; + +protected: + void parse(context_type& context) override + { + start_rule(context, rule_type::gherkin_document); + + std::size_t state = 0; + + while (true) { + auto token = context.read_token(); + state = match_token(state, token, context); + + if (token.is_eof()) { + break; + } + } + + end_rule(context, rule_type::gherkin_document); + + if (context.has_errors()) { + throw composite_parser_error(context.eptrs); + } + } + + void build(context_type& context, token& token) + { context.builder.build(token); } + + void start_rule(context_type& context, rule_type rule_type) + { + handle_ast_error( + context, + rule_type, + [&context](auto rtype) { + context.builder.start_rule(rtype); + } + ); + } + + void end_rule(context_type& context, rule_type rule_type) + { + handle_ast_error( + context, + rule_type, + [&context](auto rtype) { + context.builder.end_rule(rtype); + } + ); + } + + template + bool handle_external_error( + context_type& context, + bool default_value, + Argument&& argument, + Action&& action + ) + { + using ret_type = decltype(action(argument)); + + try { + if constexpr (std::is_same_v) { + action(argument); + return default_value; + } else { + return action(argument); + } + } catch (const composite_parser_error& e) { + for (const auto& ep : e.errors()) { + context.add_error(ep); + } + } catch (const parser_error& e) { + auto ep = new_parser_error(e); + context.add_error(ep); + } + + return default_value; + } + + template + void handle_ast_error( + context_type& context, + Argument&& argument, + Action&& action + ) + { handle_external_error(context, true, argument, action); } + + + bool match_e_o_f(context_type& context, token& token) + { + return + handle_external_error( + context, + false, + token, + [&context](auto& t) { + return context.matcher.match_e_o_f(t); + } + ); + } + + bool match_empty(context_type& context, token& token) + { + if (token.is_eof()) { + return false; + } + + return + handle_external_error( + context, + false, + token, + [&context](auto& t) { + return context.matcher.match_empty(t); + } + ); + } + + bool match_comment(context_type& context, token& token) + { + if (token.is_eof()) { + return false; + } + + return + handle_external_error( + context, + false, + token, + [&context](auto& t) { + return context.matcher.match_comment(t); + } + ); + } + + bool match_tag_line(context_type& context, token& token) + { + if (token.is_eof()) { + return false; + } + + return + handle_external_error( + context, + false, + token, + [&context](auto& t) { + return context.matcher.match_tag_line(t); + } + ); + } + + bool match_feature_line(context_type& context, token& token) + { + if (token.is_eof()) { + return false; + } + + return + handle_external_error( + context, + false, + token, + [&context](auto& t) { + return context.matcher.match_feature_line(t); + } + ); + } + + bool match_rule_line(context_type& context, token& token) + { + if (token.is_eof()) { + return false; + } + + return + handle_external_error( + context, + false, + token, + [&context](auto& t) { + return context.matcher.match_rule_line(t); + } + ); + } + + bool match_background_line(context_type& context, token& token) + { + if (token.is_eof()) { + return false; + } + + return + handle_external_error( + context, + false, + token, + [&context](auto& t) { + return context.matcher.match_background_line(t); + } + ); + } + + bool match_scenario_line(context_type& context, token& token) + { + if (token.is_eof()) { + return false; + } + + return + handle_external_error( + context, + false, + token, + [&context](auto& t) { + return context.matcher.match_scenario_line(t); + } + ); + } + + bool match_examples_line(context_type& context, token& token) + { + if (token.is_eof()) { + return false; + } + + return + handle_external_error( + context, + false, + token, + [&context](auto& t) { + return context.matcher.match_examples_line(t); + } + ); + } + + bool match_step_line(context_type& context, token& token) + { + if (token.is_eof()) { + return false; + } + + return + handle_external_error( + context, + false, + token, + [&context](auto& t) { + return context.matcher.match_step_line(t); + } + ); + } + + bool match_doc_string_separator(context_type& context, token& token) + { + if (token.is_eof()) { + return false; + } + + return + handle_external_error( + context, + false, + token, + [&context](auto& t) { + return context.matcher.match_doc_string_separator(t); + } + ); + } + + bool match_table_row(context_type& context, token& token) + { + if (token.is_eof()) { + return false; + } + + return + handle_external_error( + context, + false, + token, + [&context](auto& t) { + return context.matcher.match_table_row(t); + } + ); + } + + bool match_language(context_type& context, token& token) + { + if (token.is_eof()) { + return false; + } + + return + handle_external_error( + context, + false, + token, + [&context](auto& t) { + return context.matcher.match_language(t); + } + ); + } + + bool match_other(context_type& context, token& token) + { + if (token.is_eof()) { + return false; + } + + return + handle_external_error( + context, + false, + token, + [&context](auto& t) { + return context.matcher.match_other(t); + } + ); + } + + + bool lookahead_0(context_type& context, token& current_token) + { + current_token.detach(); + token token; + token_queue queue; + bool match = false; + + while (true) { + token = context.read_token(); + token.detach(); + queue.push_back(token); + + if (match_scenario_line(context, token) || false) { + match = true; + break; + } + + if (!(match_empty(context, token) || match_comment(context, token) || match_tag_line(context, token) || false)) { + break; + } + } + + context.push_tokens(queue); + + return match; + } + + bool lookahead_1(context_type& context, token& current_token) + { + current_token.detach(); + token token; + token_queue queue; + bool match = false; + + while (true) { + token = context.read_token(); + token.detach(); + queue.push_back(token); + + if (match_examples_line(context, token) || false) { + match = true; + break; + } + + if (!(match_empty(context, token) || match_comment(context, token) || match_tag_line(context, token) || false)) { + break; + } + } + + context.push_tokens(queue); + + return match; + } + + + // Start + std::size_t match_token_at_0(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + build(context, token); + return 42; + } + if (match_language(context, token)) { + start_rule(context, rule_type::feature); + start_rule(context, rule_type::feature_header); + build(context, token); + return 1; + } + if (match_tag_line(context, token)) { + start_rule(context, rule_type::feature); + start_rule(context, rule_type::feature_header); + start_rule(context, rule_type::tags); + build(context, token); + return 2; + } + if (match_feature_line(context, token)) { + start_rule(context, rule_type::feature); + start_rule(context, rule_type::feature_header); + build(context, token); + return 3; + } + if (match_comment(context, token)) { + build(context, token); + return 0; + } + if (match_empty(context, token)) { + build(context, token); + return 0; + } + + std::string state_comment = "State: 0 - Start"; + std::string expected_tokens = "#EOF, #Language, #TagLine, #FeatureLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 0; + } + + // GherkinDocument:0>Feature:0>FeatureHeader:0>#Language:0 + std::size_t match_token_at_1(token& token, context_type& context) + { + if (match_tag_line(context, token)) { + start_rule(context, rule_type::tags); + build(context, token); + return 2; + } + if (match_feature_line(context, token)) { + build(context, token); + return 3; + } + if (match_comment(context, token)) { + build(context, token); + return 1; + } + if (match_empty(context, token)) { + build(context, token); + return 1; + } + + std::string state_comment = "State: 1 - GherkinDocument:0>Feature:0>FeatureHeader:0>#Language:0"; + std::string expected_tokens = "#TagLine, #FeatureLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 1; + } + + // GherkinDocument:0>Feature:0>FeatureHeader:1>Tags:0>#TagLine:0 + std::size_t match_token_at_2(token& token, context_type& context) + { + if (match_tag_line(context, token)) { + build(context, token); + return 2; + } + if (match_feature_line(context, token)) { + end_rule(context, rule_type::tags); + build(context, token); + return 3; + } + if (match_comment(context, token)) { + build(context, token); + return 2; + } + if (match_empty(context, token)) { + build(context, token); + return 2; + } + + std::string state_comment = "State: 2 - GherkinDocument:0>Feature:0>FeatureHeader:1>Tags:0>#TagLine:0"; + std::string expected_tokens = "#TagLine, #FeatureLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 2; + } + + // GherkinDocument:0>Feature:0>FeatureHeader:2>#FeatureLine:0 + std::size_t match_token_at_3(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::feature_header); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_empty(context, token)) { + build(context, token); + return 3; + } + if (match_comment(context, token)) { + build(context, token); + return 5; + } + if (match_background_line(context, token)) { + end_rule(context, rule_type::feature_header); + start_rule(context, rule_type::background); + build(context, token); + return 6; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::feature_header); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::feature_header); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::feature_header); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::feature_header); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_other(context, token)) { + start_rule(context, rule_type::description); + build(context, token); + return 4; + } + + std::string state_comment = "State: 3 - GherkinDocument:0>Feature:0>FeatureHeader:2>#FeatureLine:0"; + std::string expected_tokens = "#EOF, #Empty, #Comment, #BackgroundLine, #TagLine, #ScenarioLine, #RuleLine, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 3; + } + + // GherkinDocument:0>Feature:0>FeatureHeader:3>DescriptionHelper:1>Description:0>#Other:0 + std::size_t match_token_at_4(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::feature_header); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_comment(context, token)) { + end_rule(context, rule_type::description); + build(context, token); + return 5; + } + if (match_background_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::feature_header); + start_rule(context, rule_type::background); + build(context, token); + return 6; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::feature_header); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::feature_header); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::feature_header); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::feature_header); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_other(context, token)) { + build(context, token); + return 4; + } + + std::string state_comment = "State: 4 - GherkinDocument:0>Feature:0>FeatureHeader:3>DescriptionHelper:1>Description:0>#Other:0"; + std::string expected_tokens = "#EOF, #Comment, #BackgroundLine, #TagLine, #ScenarioLine, #RuleLine, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 4; + } + + // GherkinDocument:0>Feature:0>FeatureHeader:3>DescriptionHelper:2>#Comment:0 + std::size_t match_token_at_5(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::feature_header); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_comment(context, token)) { + build(context, token); + return 5; + } + if (match_background_line(context, token)) { + end_rule(context, rule_type::feature_header); + start_rule(context, rule_type::background); + build(context, token); + return 6; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::feature_header); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::feature_header); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::feature_header); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::feature_header); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_empty(context, token)) { + build(context, token); + return 5; + } + + std::string state_comment = "State: 5 - GherkinDocument:0>Feature:0>FeatureHeader:3>DescriptionHelper:2>#Comment:0"; + std::string expected_tokens = "#EOF, #Comment, #BackgroundLine, #TagLine, #ScenarioLine, #RuleLine, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 5; + } + + // GherkinDocument:0>Feature:1>Background:0>#BackgroundLine:0 + std::size_t match_token_at_6(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::background); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_empty(context, token)) { + build(context, token); + return 6; + } + if (match_comment(context, token)) { + build(context, token); + return 8; + } + if (match_step_line(context, token)) { + start_rule(context, rule_type::step); + build(context, token); + return 9; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::background); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::background); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_other(context, token)) { + start_rule(context, rule_type::description); + build(context, token); + return 7; + } + + std::string state_comment = "State: 6 - GherkinDocument:0>Feature:1>Background:0>#BackgroundLine:0"; + std::string expected_tokens = "#EOF, #Empty, #Comment, #StepLine, #TagLine, #ScenarioLine, #RuleLine, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 6; + } + + // GherkinDocument:0>Feature:1>Background:1>DescriptionHelper:1>Description:0>#Other:0 + std::size_t match_token_at_7(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::background); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_comment(context, token)) { + end_rule(context, rule_type::description); + build(context, token); + return 8; + } + if (match_step_line(context, token)) { + end_rule(context, rule_type::description); + start_rule(context, rule_type::step); + build(context, token); + return 9; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::background); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::background); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_other(context, token)) { + build(context, token); + return 7; + } + + std::string state_comment = "State: 7 - GherkinDocument:0>Feature:1>Background:1>DescriptionHelper:1>Description:0>#Other:0"; + std::string expected_tokens = "#EOF, #Comment, #StepLine, #TagLine, #ScenarioLine, #RuleLine, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 7; + } + + // GherkinDocument:0>Feature:1>Background:1>DescriptionHelper:2>#Comment:0 + std::size_t match_token_at_8(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::background); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_comment(context, token)) { + build(context, token); + return 8; + } + if (match_step_line(context, token)) { + start_rule(context, rule_type::step); + build(context, token); + return 9; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::background); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::background); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_empty(context, token)) { + build(context, token); + return 8; + } + + std::string state_comment = "State: 8 - GherkinDocument:0>Feature:1>Background:1>DescriptionHelper:2>#Comment:0"; + std::string expected_tokens = "#EOF, #Comment, #StepLine, #TagLine, #ScenarioLine, #RuleLine, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 8; + } + + // GherkinDocument:0>Feature:1>Background:2>Step:0>#StepLine:0 + std::size_t match_token_at_9(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_table_row(context, token)) { + start_rule(context, rule_type::data_table); + build(context, token); + return 10; + } + if (match_doc_string_separator(context, token)) { + start_rule(context, rule_type::doc_string); + build(context, token); + return 49; + } + if (match_step_line(context, token)) { + end_rule(context, rule_type::step); + start_rule(context, rule_type::step); + build(context, token); + return 9; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_comment(context, token)) { + build(context, token); + return 9; + } + if (match_empty(context, token)) { + build(context, token); + return 9; + } + + std::string state_comment = "State: 9 - GherkinDocument:0>Feature:1>Background:2>Step:0>#StepLine:0"; + std::string expected_tokens = "#EOF, #TableRow, #DocStringSeparator, #StepLine, #TagLine, #ScenarioLine, #RuleLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 9; + } + + // GherkinDocument:0>Feature:1>Background:2>Step:1>StepArg:0>__alt0:0>DataTable:0>#TableRow:0 + std::size_t match_token_at_10(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_table_row(context, token)) { + build(context, token); + return 10; + } + if (match_step_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + start_rule(context, rule_type::step); + build(context, token); + return 9; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_comment(context, token)) { + build(context, token); + return 10; + } + if (match_empty(context, token)) { + build(context, token); + return 10; + } + + std::string state_comment = "State: 10 - GherkinDocument:0>Feature:1>Background:2>Step:1>StepArg:0>__alt0:0>DataTable:0>#TableRow:0"; + std::string expected_tokens = "#EOF, #TableRow, #StepLine, #TagLine, #ScenarioLine, #RuleLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 10; + } + + // GherkinDocument:0>Feature:2>ScenarioDefinition:0>Tags:0>#TagLine:0 + std::size_t match_token_at_11(token& token, context_type& context) + { + if (match_tag_line(context, token)) { + build(context, token); + return 11; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::tags); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_comment(context, token)) { + build(context, token); + return 11; + } + if (match_empty(context, token)) { + build(context, token); + return 11; + } + + std::string state_comment = "State: 11 - GherkinDocument:0>Feature:2>ScenarioDefinition:0>Tags:0>#TagLine:0"; + std::string expected_tokens = "#TagLine, #ScenarioLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 11; + } + + // GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:0>#ScenarioLine:0 + std::size_t match_token_at_12(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_empty(context, token)) { + build(context, token); + return 12; + } + if (match_comment(context, token)) { + build(context, token); + return 14; + } + if (match_step_line(context, token)) { + start_rule(context, rule_type::step); + build(context, token); + return 15; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 17; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 18; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_other(context, token)) { + start_rule(context, rule_type::description); + build(context, token); + return 13; + } + + std::string state_comment = "State: 12 - GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:0>#ScenarioLine:0"; + std::string expected_tokens = "#EOF, #Empty, #Comment, #StepLine, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 12; + } + + // GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:1>DescriptionHelper:1>Description:0>#Other:0 + std::size_t match_token_at_13(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_comment(context, token)) { + end_rule(context, rule_type::description); + build(context, token); + return 14; + } + if (match_step_line(context, token)) { + end_rule(context, rule_type::description); + start_rule(context, rule_type::step); + build(context, token); + return 15; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + end_rule(context, rule_type::description); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 17; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::description); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 18; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_other(context, token)) { + build(context, token); + return 13; + } + + std::string state_comment = "State: 13 - GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:1>DescriptionHelper:1>Description:0>#Other:0"; + std::string expected_tokens = "#EOF, #Comment, #StepLine, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 13; + } + + // GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:1>DescriptionHelper:2>#Comment:0 + std::size_t match_token_at_14(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_comment(context, token)) { + build(context, token); + return 14; + } + if (match_step_line(context, token)) { + start_rule(context, rule_type::step); + build(context, token); + return 15; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 17; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 18; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_empty(context, token)) { + build(context, token); + return 14; + } + + std::string state_comment = "State: 14 - GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:1>DescriptionHelper:2>#Comment:0"; + std::string expected_tokens = "#EOF, #Comment, #StepLine, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 14; + } + + // GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:2>Step:0>#StepLine:0 + std::size_t match_token_at_15(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_table_row(context, token)) { + start_rule(context, rule_type::data_table); + build(context, token); + return 16; + } + if (match_doc_string_separator(context, token)) { + start_rule(context, rule_type::doc_string); + build(context, token); + return 47; + } + if (match_step_line(context, token)) { + end_rule(context, rule_type::step); + start_rule(context, rule_type::step); + build(context, token); + return 15; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + end_rule(context, rule_type::step); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 17; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::step); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 18; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_comment(context, token)) { + build(context, token); + return 15; + } + if (match_empty(context, token)) { + build(context, token); + return 15; + } + + std::string state_comment = "State: 15 - GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:2>Step:0>#StepLine:0"; + std::string expected_tokens = "#EOF, #TableRow, #DocStringSeparator, #StepLine, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 15; + } + + // GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:2>Step:1>StepArg:0>__alt0:0>DataTable:0>#TableRow:0 + std::size_t match_token_at_16(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_table_row(context, token)) { + build(context, token); + return 16; + } + if (match_step_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + start_rule(context, rule_type::step); + build(context, token); + return 15; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 17; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 18; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_comment(context, token)) { + build(context, token); + return 16; + } + if (match_empty(context, token)) { + build(context, token); + return 16; + } + + std::string state_comment = "State: 16 - GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:2>Step:1>StepArg:0>__alt0:0>DataTable:0>#TableRow:0"; + std::string expected_tokens = "#EOF, #TableRow, #StepLine, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 16; + } + + // GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:0>Tags:0>#TagLine:0 + std::size_t match_token_at_17(token& token, context_type& context) + { + if (match_tag_line(context, token)) { + build(context, token); + return 17; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::tags); + start_rule(context, rule_type::examples); + build(context, token); + return 18; + } + if (match_comment(context, token)) { + build(context, token); + return 17; + } + if (match_empty(context, token)) { + build(context, token); + return 17; + } + + std::string state_comment = "State: 17 - GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:0>Tags:0>#TagLine:0"; + std::string expected_tokens = "#TagLine, #ExamplesLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 17; + } + + // GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:1>Examples:0>#ExamplesLine:0 + std::size_t match_token_at_18(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_empty(context, token)) { + build(context, token); + return 18; + } + if (match_comment(context, token)) { + build(context, token); + return 20; + } + if (match_table_row(context, token)) { + start_rule(context, rule_type::examples_table); + build(context, token); + return 21; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 17; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 18; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_other(context, token)) { + start_rule(context, rule_type::description); + build(context, token); + return 19; + } + + std::string state_comment = "State: 18 - GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:1>Examples:0>#ExamplesLine:0"; + std::string expected_tokens = "#EOF, #Empty, #Comment, #TableRow, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 18; + } + + // GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:1>Examples:1>DescriptionHelper:1>Description:0>#Other:0 + std::size_t match_token_at_19(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_comment(context, token)) { + end_rule(context, rule_type::description); + build(context, token); + return 20; + } + if (match_table_row(context, token)) { + end_rule(context, rule_type::description); + start_rule(context, rule_type::examples_table); + build(context, token); + return 21; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 17; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 18; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_other(context, token)) { + build(context, token); + return 19; + } + + std::string state_comment = "State: 19 - GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:1>Examples:1>DescriptionHelper:1>Description:0>#Other:0"; + std::string expected_tokens = "#EOF, #Comment, #TableRow, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 19; + } + + // GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:1>Examples:1>DescriptionHelper:2>#Comment:0 + std::size_t match_token_at_20(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_comment(context, token)) { + build(context, token); + return 20; + } + if (match_table_row(context, token)) { + start_rule(context, rule_type::examples_table); + build(context, token); + return 21; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 17; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 18; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_empty(context, token)) { + build(context, token); + return 20; + } + + std::string state_comment = "State: 20 - GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:1>Examples:1>DescriptionHelper:2>#Comment:0"; + std::string expected_tokens = "#EOF, #Comment, #TableRow, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 20; + } + + // GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:1>Examples:2>ExamplesTable:0>#TableRow:0 + std::size_t match_token_at_21(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::examples_table); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_table_row(context, token)) { + build(context, token); + return 21; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + end_rule(context, rule_type::examples_table); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 17; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::examples_table); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::examples_table); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::examples_table); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 18; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::examples_table); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::examples_table); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_comment(context, token)) { + build(context, token); + return 21; + } + if (match_empty(context, token)) { + build(context, token); + return 21; + } + + std::string state_comment = "State: 21 - GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:1>Examples:2>ExamplesTable:0>#TableRow:0"; + std::string expected_tokens = "#EOF, #TableRow, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 21; + } + + // GherkinDocument:0>Feature:3>Rule:0>RuleHeader:0>Tags:0>#TagLine:0 + std::size_t match_token_at_22(token& token, context_type& context) + { + if (match_tag_line(context, token)) { + build(context, token); + return 22; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::tags); + build(context, token); + return 23; + } + if (match_comment(context, token)) { + build(context, token); + return 22; + } + if (match_empty(context, token)) { + build(context, token); + return 22; + } + + std::string state_comment = "State: 22 - GherkinDocument:0>Feature:3>Rule:0>RuleHeader:0>Tags:0>#TagLine:0"; + std::string expected_tokens = "#TagLine, #RuleLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 22; + } + + // GherkinDocument:0>Feature:3>Rule:0>RuleHeader:1>#RuleLine:0 + std::size_t match_token_at_23(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::rule_header); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_empty(context, token)) { + build(context, token); + return 23; + } + if (match_comment(context, token)) { + build(context, token); + return 25; + } + if (match_background_line(context, token)) { + end_rule(context, rule_type::rule_header); + start_rule(context, rule_type::background); + build(context, token); + return 26; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::rule_header); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::rule_header); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::rule_header); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::rule_header); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_other(context, token)) { + start_rule(context, rule_type::description); + build(context, token); + return 24; + } + + std::string state_comment = "State: 23 - GherkinDocument:0>Feature:3>Rule:0>RuleHeader:1>#RuleLine:0"; + std::string expected_tokens = "#EOF, #Empty, #Comment, #BackgroundLine, #TagLine, #ScenarioLine, #RuleLine, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 23; + } + + // GherkinDocument:0>Feature:3>Rule:0>RuleHeader:2>DescriptionHelper:1>Description:0>#Other:0 + std::size_t match_token_at_24(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::rule_header); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_comment(context, token)) { + end_rule(context, rule_type::description); + build(context, token); + return 25; + } + if (match_background_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::rule_header); + start_rule(context, rule_type::background); + build(context, token); + return 26; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::rule_header); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::rule_header); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::rule_header); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::rule_header); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_other(context, token)) { + build(context, token); + return 24; + } + + std::string state_comment = "State: 24 - GherkinDocument:0>Feature:3>Rule:0>RuleHeader:2>DescriptionHelper:1>Description:0>#Other:0"; + std::string expected_tokens = "#EOF, #Comment, #BackgroundLine, #TagLine, #ScenarioLine, #RuleLine, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 24; + } + + // GherkinDocument:0>Feature:3>Rule:0>RuleHeader:2>DescriptionHelper:2>#Comment:0 + std::size_t match_token_at_25(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::rule_header); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_comment(context, token)) { + build(context, token); + return 25; + } + if (match_background_line(context, token)) { + end_rule(context, rule_type::rule_header); + start_rule(context, rule_type::background); + build(context, token); + return 26; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::rule_header); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::rule_header); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::rule_header); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::rule_header); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_empty(context, token)) { + build(context, token); + return 25; + } + + std::string state_comment = "State: 25 - GherkinDocument:0>Feature:3>Rule:0>RuleHeader:2>DescriptionHelper:2>#Comment:0"; + std::string expected_tokens = "#EOF, #Comment, #BackgroundLine, #TagLine, #ScenarioLine, #RuleLine, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 25; + } + + // GherkinDocument:0>Feature:3>Rule:1>Background:0>#BackgroundLine:0 + std::size_t match_token_at_26(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_empty(context, token)) { + build(context, token); + return 26; + } + if (match_comment(context, token)) { + build(context, token); + return 28; + } + if (match_step_line(context, token)) { + start_rule(context, rule_type::step); + build(context, token); + return 29; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_other(context, token)) { + start_rule(context, rule_type::description); + build(context, token); + return 27; + } + + std::string state_comment = "State: 26 - GherkinDocument:0>Feature:3>Rule:1>Background:0>#BackgroundLine:0"; + std::string expected_tokens = "#EOF, #Empty, #Comment, #StepLine, #TagLine, #ScenarioLine, #RuleLine, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 26; + } + + // GherkinDocument:0>Feature:3>Rule:1>Background:1>DescriptionHelper:1>Description:0>#Other:0 + std::size_t match_token_at_27(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_comment(context, token)) { + end_rule(context, rule_type::description); + build(context, token); + return 28; + } + if (match_step_line(context, token)) { + end_rule(context, rule_type::description); + start_rule(context, rule_type::step); + build(context, token); + return 29; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_other(context, token)) { + build(context, token); + return 27; + } + + std::string state_comment = "State: 27 - GherkinDocument:0>Feature:3>Rule:1>Background:1>DescriptionHelper:1>Description:0>#Other:0"; + std::string expected_tokens = "#EOF, #Comment, #StepLine, #TagLine, #ScenarioLine, #RuleLine, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 27; + } + + // GherkinDocument:0>Feature:3>Rule:1>Background:1>DescriptionHelper:2>#Comment:0 + std::size_t match_token_at_28(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_comment(context, token)) { + build(context, token); + return 28; + } + if (match_step_line(context, token)) { + start_rule(context, rule_type::step); + build(context, token); + return 29; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_empty(context, token)) { + build(context, token); + return 28; + } + + std::string state_comment = "State: 28 - GherkinDocument:0>Feature:3>Rule:1>Background:1>DescriptionHelper:2>#Comment:0"; + std::string expected_tokens = "#EOF, #Comment, #StepLine, #TagLine, #ScenarioLine, #RuleLine, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 28; + } + + // GherkinDocument:0>Feature:3>Rule:1>Background:2>Step:0>#StepLine:0 + std::size_t match_token_at_29(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_table_row(context, token)) { + start_rule(context, rule_type::data_table); + build(context, token); + return 30; + } + if (match_doc_string_separator(context, token)) { + start_rule(context, rule_type::doc_string); + build(context, token); + return 45; + } + if (match_step_line(context, token)) { + end_rule(context, rule_type::step); + start_rule(context, rule_type::step); + build(context, token); + return 29; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_comment(context, token)) { + build(context, token); + return 29; + } + if (match_empty(context, token)) { + build(context, token); + return 29; + } + + std::string state_comment = "State: 29 - GherkinDocument:0>Feature:3>Rule:1>Background:2>Step:0>#StepLine:0"; + std::string expected_tokens = "#EOF, #TableRow, #DocStringSeparator, #StepLine, #TagLine, #ScenarioLine, #RuleLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 29; + } + + // GherkinDocument:0>Feature:3>Rule:1>Background:2>Step:1>StepArg:0>__alt0:0>DataTable:0>#TableRow:0 + std::size_t match_token_at_30(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_table_row(context, token)) { + build(context, token); + return 30; + } + if (match_step_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + start_rule(context, rule_type::step); + build(context, token); + return 29; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_comment(context, token)) { + build(context, token); + return 30; + } + if (match_empty(context, token)) { + build(context, token); + return 30; + } + + std::string state_comment = "State: 30 - GherkinDocument:0>Feature:3>Rule:1>Background:2>Step:1>StepArg:0>__alt0:0>DataTable:0>#TableRow:0"; + std::string expected_tokens = "#EOF, #TableRow, #StepLine, #TagLine, #ScenarioLine, #RuleLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 30; + } + + // GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:0>Tags:0>#TagLine:0 + std::size_t match_token_at_31(token& token, context_type& context) + { + if (match_tag_line(context, token)) { + build(context, token); + return 31; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::tags); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_comment(context, token)) { + build(context, token); + return 31; + } + if (match_empty(context, token)) { + build(context, token); + return 31; + } + + std::string state_comment = "State: 31 - GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:0>Tags:0>#TagLine:0"; + std::string expected_tokens = "#TagLine, #ScenarioLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 31; + } + + // GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:0>#ScenarioLine:0 + std::size_t match_token_at_32(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_empty(context, token)) { + build(context, token); + return 32; + } + if (match_comment(context, token)) { + build(context, token); + return 34; + } + if (match_step_line(context, token)) { + start_rule(context, rule_type::step); + build(context, token); + return 35; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 37; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 38; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_other(context, token)) { + start_rule(context, rule_type::description); + build(context, token); + return 33; + } + + std::string state_comment = "State: 32 - GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:0>#ScenarioLine:0"; + std::string expected_tokens = "#EOF, #Empty, #Comment, #StepLine, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 32; + } + + // GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:1>DescriptionHelper:1>Description:0>#Other:0 + std::size_t match_token_at_33(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_comment(context, token)) { + end_rule(context, rule_type::description); + build(context, token); + return 34; + } + if (match_step_line(context, token)) { + end_rule(context, rule_type::description); + start_rule(context, rule_type::step); + build(context, token); + return 35; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + end_rule(context, rule_type::description); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 37; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::description); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 38; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_other(context, token)) { + build(context, token); + return 33; + } + + std::string state_comment = "State: 33 - GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:1>DescriptionHelper:1>Description:0>#Other:0"; + std::string expected_tokens = "#EOF, #Comment, #StepLine, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 33; + } + + // GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:1>DescriptionHelper:2>#Comment:0 + std::size_t match_token_at_34(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_comment(context, token)) { + build(context, token); + return 34; + } + if (match_step_line(context, token)) { + start_rule(context, rule_type::step); + build(context, token); + return 35; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 37; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 38; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_empty(context, token)) { + build(context, token); + return 34; + } + + std::string state_comment = "State: 34 - GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:1>DescriptionHelper:2>#Comment:0"; + std::string expected_tokens = "#EOF, #Comment, #StepLine, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 34; + } + + // GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:2>Step:0>#StepLine:0 + std::size_t match_token_at_35(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_table_row(context, token)) { + start_rule(context, rule_type::data_table); + build(context, token); + return 36; + } + if (match_doc_string_separator(context, token)) { + start_rule(context, rule_type::doc_string); + build(context, token); + return 43; + } + if (match_step_line(context, token)) { + end_rule(context, rule_type::step); + start_rule(context, rule_type::step); + build(context, token); + return 35; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + end_rule(context, rule_type::step); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 37; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::step); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 38; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_comment(context, token)) { + build(context, token); + return 35; + } + if (match_empty(context, token)) { + build(context, token); + return 35; + } + + std::string state_comment = "State: 35 - GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:2>Step:0>#StepLine:0"; + std::string expected_tokens = "#EOF, #TableRow, #DocStringSeparator, #StepLine, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 35; + } + + // GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:2>Step:1>StepArg:0>__alt0:0>DataTable:0>#TableRow:0 + std::size_t match_token_at_36(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_table_row(context, token)) { + build(context, token); + return 36; + } + if (match_step_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + start_rule(context, rule_type::step); + build(context, token); + return 35; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 37; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 38; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::data_table); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_comment(context, token)) { + build(context, token); + return 36; + } + if (match_empty(context, token)) { + build(context, token); + return 36; + } + + std::string state_comment = "State: 36 - GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:2>Step:1>StepArg:0>__alt0:0>DataTable:0>#TableRow:0"; + std::string expected_tokens = "#EOF, #TableRow, #StepLine, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 36; + } + + // GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:0>Tags:0>#TagLine:0 + std::size_t match_token_at_37(token& token, context_type& context) + { + if (match_tag_line(context, token)) { + build(context, token); + return 37; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::tags); + start_rule(context, rule_type::examples); + build(context, token); + return 38; + } + if (match_comment(context, token)) { + build(context, token); + return 37; + } + if (match_empty(context, token)) { + build(context, token); + return 37; + } + + std::string state_comment = "State: 37 - GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:0>Tags:0>#TagLine:0"; + std::string expected_tokens = "#TagLine, #ExamplesLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 37; + } + + // GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:1>Examples:0>#ExamplesLine:0 + std::size_t match_token_at_38(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_empty(context, token)) { + build(context, token); + return 38; + } + if (match_comment(context, token)) { + build(context, token); + return 40; + } + if (match_table_row(context, token)) { + start_rule(context, rule_type::examples_table); + build(context, token); + return 41; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 37; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 38; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_other(context, token)) { + start_rule(context, rule_type::description); + build(context, token); + return 39; + } + + std::string state_comment = "State: 38 - GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:1>Examples:0>#ExamplesLine:0"; + std::string expected_tokens = "#EOF, #Empty, #Comment, #TableRow, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 38; + } + + // GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:1>Examples:1>DescriptionHelper:1>Description:0>#Other:0 + std::size_t match_token_at_39(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_comment(context, token)) { + end_rule(context, rule_type::description); + build(context, token); + return 40; + } + if (match_table_row(context, token)) { + end_rule(context, rule_type::description); + start_rule(context, rule_type::examples_table); + build(context, token); + return 41; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 37; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 38; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::description); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_other(context, token)) { + build(context, token); + return 39; + } + + std::string state_comment = "State: 39 - GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:1>Examples:1>DescriptionHelper:1>Description:0>#Other:0"; + std::string expected_tokens = "#EOF, #Comment, #TableRow, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 39; + } + + // GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:1>Examples:1>DescriptionHelper:2>#Comment:0 + std::size_t match_token_at_40(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_comment(context, token)) { + build(context, token); + return 40; + } + if (match_table_row(context, token)) { + start_rule(context, rule_type::examples_table); + build(context, token); + return 41; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 37; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 38; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_empty(context, token)) { + build(context, token); + return 40; + } + + std::string state_comment = "State: 40 - GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:1>Examples:1>DescriptionHelper:2>#Comment:0"; + std::string expected_tokens = "#EOF, #Comment, #TableRow, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 40; + } + + // GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:1>Examples:2>ExamplesTable:0>#TableRow:0 + std::size_t match_token_at_41(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::examples_table); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_table_row(context, token)) { + build(context, token); + return 41; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + end_rule(context, rule_type::examples_table); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 37; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::examples_table); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::examples_table); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::examples_table); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 38; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::examples_table); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::examples_table); + end_rule(context, rule_type::examples); + end_rule(context, rule_type::examples_definition); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_comment(context, token)) { + build(context, token); + return 41; + } + if (match_empty(context, token)) { + build(context, token); + return 41; + } + + std::string state_comment = "State: 41 - GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:3>ExamplesDefinition:1>Examples:2>ExamplesTable:0>#TableRow:0"; + std::string expected_tokens = "#EOF, #TableRow, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 41; + } + + // GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:2>Step:1>StepArg:0>__alt0:1>DocString:0>#DocStringSeparator:0 + std::size_t match_token_at_43(token& token, context_type& context) + { + if (match_doc_string_separator(context, token)) { + build(context, token); + return 44; + } + if (match_other(context, token)) { + build(context, token); + return 43; + } + + std::string state_comment = "State: 43 - GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:2>Step:1>StepArg:0>__alt0:1>DocString:0>#DocStringSeparator:0"; + std::string expected_tokens = "#DocStringSeparator, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 43; + } + + // GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:2>Step:1>StepArg:0>__alt0:1>DocString:2>#DocStringSeparator:0 + std::size_t match_token_at_44(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_step_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + start_rule(context, rule_type::step); + build(context, token); + return 35; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 37; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 38; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_comment(context, token)) { + build(context, token); + return 44; + } + if (match_empty(context, token)) { + build(context, token); + return 44; + } + + std::string state_comment = "State: 44 - GherkinDocument:0>Feature:3>Rule:2>ScenarioDefinition:1>Scenario:2>Step:1>StepArg:0>__alt0:1>DocString:2>#DocStringSeparator:0"; + std::string expected_tokens = "#EOF, #StepLine, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 44; + } + + // GherkinDocument:0>Feature:3>Rule:1>Background:2>Step:1>StepArg:0>__alt0:1>DocString:0>#DocStringSeparator:0 + std::size_t match_token_at_45(token& token, context_type& context) + { + if (match_doc_string_separator(context, token)) { + build(context, token); + return 46; + } + if (match_other(context, token)) { + build(context, token); + return 45; + } + + std::string state_comment = "State: 45 - GherkinDocument:0>Feature:3>Rule:1>Background:2>Step:1>StepArg:0>__alt0:1>DocString:0>#DocStringSeparator:0"; + std::string expected_tokens = "#DocStringSeparator, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 45; + } + + // GherkinDocument:0>Feature:3>Rule:1>Background:2>Step:1>StepArg:0>__alt0:1>DocString:2>#DocStringSeparator:0 + std::size_t match_token_at_46(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_step_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + start_rule(context, rule_type::step); + build(context, token); + return 29; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 31; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 32; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + end_rule(context, rule_type::rule); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_comment(context, token)) { + build(context, token); + return 46; + } + if (match_empty(context, token)) { + build(context, token); + return 46; + } + + std::string state_comment = "State: 46 - GherkinDocument:0>Feature:3>Rule:1>Background:2>Step:1>StepArg:0>__alt0:1>DocString:2>#DocStringSeparator:0"; + std::string expected_tokens = "#EOF, #StepLine, #TagLine, #ScenarioLine, #RuleLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 46; + } + + // GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:2>Step:1>StepArg:0>__alt0:1>DocString:0>#DocStringSeparator:0 + std::size_t match_token_at_47(token& token, context_type& context) + { + if (match_doc_string_separator(context, token)) { + build(context, token); + return 48; + } + if (match_other(context, token)) { + build(context, token); + return 47; + } + + std::string state_comment = "State: 47 - GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:2>Step:1>StepArg:0>__alt0:1>DocString:0>#DocStringSeparator:0"; + std::string expected_tokens = "#DocStringSeparator, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 47; + } + + // GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:2>Step:1>StepArg:0>__alt0:1>DocString:2>#DocStringSeparator:0 + std::size_t match_token_at_48(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_step_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + start_rule(context, rule_type::step); + build(context, token); + return 15; + } + if (match_tag_line(context, token)) { + if (lookahead_1(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 17; + } + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_examples_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + start_rule(context, rule_type::examples_definition); + start_rule(context, rule_type::examples); + build(context, token); + return 18; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::scenario); + end_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_comment(context, token)) { + build(context, token); + return 48; + } + if (match_empty(context, token)) { + build(context, token); + return 48; + } + + std::string state_comment = "State: 48 - GherkinDocument:0>Feature:2>ScenarioDefinition:1>Scenario:2>Step:1>StepArg:0>__alt0:1>DocString:2>#DocStringSeparator:0"; + std::string expected_tokens = "#EOF, #StepLine, #TagLine, #ExamplesLine, #ScenarioLine, #RuleLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 48; + } + + // GherkinDocument:0>Feature:1>Background:2>Step:1>StepArg:0>__alt0:1>DocString:0>#DocStringSeparator:0 + std::size_t match_token_at_49(token& token, context_type& context) + { + if (match_doc_string_separator(context, token)) { + build(context, token); + return 50; + } + if (match_other(context, token)) { + build(context, token); + return 49; + } + + std::string state_comment = "State: 49 - GherkinDocument:0>Feature:1>Background:2>Step:1>StepArg:0>__alt0:1>DocString:0>#DocStringSeparator:0"; + std::string expected_tokens = "#DocStringSeparator, #Other"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 49; + } + + // GherkinDocument:0>Feature:1>Background:2>Step:1>StepArg:0>__alt0:1>DocString:2>#DocStringSeparator:0 + std::size_t match_token_at_50(token& token, context_type& context) + { + if (match_e_o_f(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + end_rule(context, rule_type::feature); + build(context, token); + return 42; + } + if (match_step_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + start_rule(context, rule_type::step); + build(context, token); + return 9; + } + if (match_tag_line(context, token)) { + if (lookahead_0(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::tags); + build(context, token); + return 11; + } + } + if (match_tag_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + start_rule(context, rule_type::tags); + build(context, token); + return 22; + } + if (match_scenario_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::scenario_definition); + start_rule(context, rule_type::scenario); + build(context, token); + return 12; + } + if (match_rule_line(context, token)) { + end_rule(context, rule_type::doc_string); + end_rule(context, rule_type::step); + end_rule(context, rule_type::background); + start_rule(context, rule_type::rule); + start_rule(context, rule_type::rule_header); + build(context, token); + return 23; + } + if (match_comment(context, token)) { + build(context, token); + return 50; + } + if (match_empty(context, token)) { + build(context, token); + return 50; + } + + std::string state_comment = "State: 50 - GherkinDocument:0>Feature:1>Background:2>Step:1>StepArg:0>__alt0:1>DocString:2>#DocStringSeparator:0"; + std::string expected_tokens = "#EOF, #StepLine, #TagLine, #ScenarioLine, #RuleLine, #Comment, #Empty"; + + auto ep = + token.is_eof() + ? new_parser_error( + token, expected_tokens, state_comment + ) + : new_parser_error( + token, expected_tokens, state_comment + ) + ; + + if (context.stop_at_first_error) { + throw *ep; + } + + context.add_error(std::move(ep)); + + return 50; + } + + std::size_t match_token(std::size_t state, token& token, context_type& context) + { + switch (state) { + case 0: + return match_token_at_0(token, context); + case 1: + return match_token_at_1(token, context); + case 2: + return match_token_at_2(token, context); + case 3: + return match_token_at_3(token, context); + case 4: + return match_token_at_4(token, context); + case 5: + return match_token_at_5(token, context); + case 6: + return match_token_at_6(token, context); + case 7: + return match_token_at_7(token, context); + case 8: + return match_token_at_8(token, context); + case 9: + return match_token_at_9(token, context); + case 10: + return match_token_at_10(token, context); + case 11: + return match_token_at_11(token, context); + case 12: + return match_token_at_12(token, context); + case 13: + return match_token_at_13(token, context); + case 14: + return match_token_at_14(token, context); + case 15: + return match_token_at_15(token, context); + case 16: + return match_token_at_16(token, context); + case 17: + return match_token_at_17(token, context); + case 18: + return match_token_at_18(token, context); + case 19: + return match_token_at_19(token, context); + case 20: + return match_token_at_20(token, context); + case 21: + return match_token_at_21(token, context); + case 22: + return match_token_at_22(token, context); + case 23: + return match_token_at_23(token, context); + case 24: + return match_token_at_24(token, context); + case 25: + return match_token_at_25(token, context); + case 26: + return match_token_at_26(token, context); + case 27: + return match_token_at_27(token, context); + case 28: + return match_token_at_28(token, context); + case 29: + return match_token_at_29(token, context); + case 30: + return match_token_at_30(token, context); + case 31: + return match_token_at_31(token, context); + case 32: + return match_token_at_32(token, context); + case 33: + return match_token_at_33(token, context); + case 34: + return match_token_at_34(token, context); + case 35: + return match_token_at_35(token, context); + case 36: + return match_token_at_36(token, context); + case 37: + return match_token_at_37(token, context); + case 38: + return match_token_at_38(token, context); + case 39: + return match_token_at_39(token, context); + case 40: + return match_token_at_40(token, context); + case 41: + return match_token_at_41(token, context); + case 43: + return match_token_at_43(token, context); + case 44: + return match_token_at_44(token, context); + case 45: + return match_token_at_45(token, context); + case 46: + return match_token_at_46(token, context); + case 47: + return match_token_at_47(token, context); + case 48: + return match_token_at_48(token, context); + case 49: + return match_token_at_49(token, context); + case 50: + return match_token_at_50(token, context); + default: + throw + std::runtime_error( + "invalid operation: " + std::to_string(state) + ); + + return -1; + } + } +}; + +} diff --git a/cpp/include/gherkin/parser_base.hpp b/cpp/include/gherkin/parser_base.hpp new file mode 100644 index 000000000..68afa814a --- /dev/null +++ b/cpp/include/gherkin/parser_base.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include + +namespace gherkin { + +namespace cms = cucumber::messages; + +template < + typename Builder = ast_builder, + typename Scanner = token_scanner, + typename Matcher = token_matcher +> +class parser_base +{ +public: + using result_type = typename Builder::result_type; + using context_type = parser_context; + + parser_base() + {} + + parser_base(id_generator_ptr idp) + : builder_(idp) + {} + + virtual ~parser_base() + {} + + result_type parse(std::string_view uri, std::string_view data) + { + reset(uri, data); + + context_type context{ + .builder = builder_, + .scanner = scanner_, + .matcher = matcher_ + }; + + parse(context); + + return get_result(); + } + +protected: + void reset(std::string_view uri, std::string_view data) + { + builder_.reset(uri); + scanner_.reset(data); + matcher_.reset(); + } + + result_type get_result() const + { return builder_.get_result(); } + + virtual void parse(context_type& context) = 0; + + Builder builder_; + Scanner scanner_; + Matcher matcher_; +}; + +} diff --git a/cpp/include/gherkin/parser_context.hpp b/cpp/include/gherkin/parser_context.hpp new file mode 100644 index 000000000..2fd0cdb8c --- /dev/null +++ b/cpp/include/gherkin/parser_context.hpp @@ -0,0 +1,62 @@ +#include +#include +#include +#include + +namespace gherkin { + +template < + typename Builder, + typename Scanner, + typename Matcher +> +struct parser_context +{ + Builder& builder; + Scanner& scanner; + Matcher& matcher; + + token_queue queue; + parser_error_ptrs eptrs; + bool stop_at_first_error = false; + std::size_t max_errors = 10; + + bool has_token() const + { return !queue.empty(); } + + token pop_token() + { + auto t = std::move(queue.front()); + queue.pop_front(); + + return t; + } + + token read_token() + { return has_token() ? pop_token() : scanner.read(); } + + void push_tokens(const token_queue& q) + { queue.insert(queue.end(), q.begin(), q.end()); } + + bool has_errors() const + { return !eptrs.empty(); } + + void add_error(parser_error_ptr ep) + { + for (const auto& p : eptrs) { + if (p->same_message(*ep)) { + return; + } + + std::cerr << "not duplicate" << std::endl; + } + + eptrs.emplace_back(std::move(ep)); + + if (eptrs.size() > max_errors) { + throw composite_parser_error(eptrs); + } + } +}; + +} diff --git a/cpp/include/gherkin/parser_info.hpp b/cpp/include/gherkin/parser_info.hpp new file mode 100644 index 000000000..0ef64b5b5 --- /dev/null +++ b/cpp/include/gherkin/parser_info.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +namespace gherkin { + +using id_generator_func = std::function; + +struct parser_info +{ + std::string language = "en"; + id_generator_func id_generator; +}; + +} diff --git a/cpp/include/gherkin/pickle_compiler.hpp b/cpp/include/gherkin/pickle_compiler.hpp new file mode 100644 index 000000000..b471ab01c --- /dev/null +++ b/cpp/include/gherkin/pickle_compiler.hpp @@ -0,0 +1,99 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace gherkin { + +class pickle_compiler +{ +public: + pickle_compiler(); + pickle_compiler(id_generator_ptr idp); + + virtual ~pickle_compiler(); + + pickles compile( + const cms::gherkin_document& d, + const std::string& uri, + pickle_cb sink = {} + ); + +private: + void compile_feature( + pickle_compiler_context& ctx, + const cms::feature& f, + const std::string& language, + const std::string& uri + ); + + void compile_rule( + pickle_compiler_context& ctx, + const cms::rule& r, + const tags& parent_tags, + const steps& background_steps, + const std::string& language, + const std::string& uri + ); + + void compile_scenario( + pickle_compiler_context& ctx, + const cms::scenario& s, + const tags& parent_tags, + const steps& background_steps, + const std::string& language, + const std::string& uri + ); + + void compile_scenario_outline( + pickle_compiler_context& ctx, + const cms::scenario& s, + const tags& parent_tags, + const steps& background_steps, + const std::string& language, + const std::string& uri + ); + + cms::pickle_table make_pickle_table( + const cms::data_table& dt, + const table_cells& variable_cells, + const table_cells& value_cells + ); + + cms::pickle_doc_string make_pickle_doc_string( + const cms::doc_string& ds, + const table_cells& variable_cells, + const table_cells& value_cells + ); + + cms::pickle_step make_pickle_step( + const cms::step& step, + const table_cells& variable_cells, + const cms::table_row* value_row_ptr, + cms::step_keyword_type keyword_type + ); + + cms::pickle_step make_pickle_step( + const cms::step& step, + cms::step_keyword_type keyword_type + ); + + pickle_tags make_pickle_tags(const tags& tags); + + std::string interpolate( + const std::string& name, + const table_cells& variable_cells, + const table_cells& value_cells + ); + + std::string next_id(); + + id_generator_ptr idp_; +}; + +} diff --git a/cpp/include/gherkin/pickle_compiler_context.hpp b/cpp/include/gherkin/pickle_compiler_context.hpp new file mode 100644 index 000000000..7f8600107 --- /dev/null +++ b/cpp/include/gherkin/pickle_compiler_context.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace gherkin { + +struct pickle_compiler_context +{ + std::string next_id(); + + void add_pickle(cms::pickle& p); + + id_generator_ptr idp; + pickle_cb sink; + gherkin::pickles pickles; + std::size_t id_counter_ = 0; +}; + +} diff --git a/cpp/include/gherkin/regex.hpp b/cpp/include/gherkin/regex.hpp new file mode 100644 index 000000000..e17c9dd4c --- /dev/null +++ b/cpp/include/gherkin/regex.hpp @@ -0,0 +1,192 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace gherkin { + +struct regex_result +{ + bool match = false; + string_views matches; +}; + +void +split( + const std::string& re, + const std::string& expr, + strings& list +); + +strings +split(const std::string& re, const std::string& expr); + +std::string +subst(const std::string& s, const std::string& re, const std::string& what = {}); + +void +subst(std::string& s, const std::string& re, const std::string& what = {}); + +namespace detail { + +struct null_arg{}; + +template +auto +extract_submatch(const SubMatch& sm, Arg&& a) +{ + using arg_type = std::decay_t; + using sv_type = std::basic_string_view; + + constexpr bool is_string = + std::is_same_v> + || + std::is_same_v> + ; + + constexpr bool is_number = + std::is_integral_v + || + std::is_floating_point_v + ; + + sv_type sv{sm.first, static_cast(sm.length())}; + + if constexpr (is_string) { + a.assign(sv); + } else if constexpr (is_number) { + auto [p, ec] = std::from_chars(sv.begin(), sv.end(), a); + + die_unless( + ec == std::errc(), + "failed to convert \"", + sv, + "\" to ", + declname(a) + ); + } else if constexpr (!std::is_same_v) { + die("unsupported argument: ", declname(a)); + } + + return sv; +} + +template +void +check_match_args(MatchResult&& m) +{ + if constexpr (N > 0) { + auto expected = m.size() - 1; + + die_unless( + N == expected, + "incorrect match args: ", + "expected ", expected, + ", got ", N + ); + } +} + +template +void +extract_submatches(MatchResult&& m, Args&&... args) +{ + constexpr auto nargs = sizeof...(args); + + check_match_args(m); + + auto mit = m.begin(); + + if constexpr (nargs > 0) { + (extract_submatch(*++mit, std::forward(args)), ...); + } +} + +template < + typename CharT, + typename MatchResult +> +void +extract_submatches( + MatchResult&& m, + std::vector>& vs +) +{ + auto mit = m.begin(); + + while (++mit != m.end()) { + vs.push_back(std::string_view{mit->first, mit->second}); + } +} + +} // namespace detail + +template +bool +full_match( + std::basic_string_view e, + const std::basic_regex& re, + Args&&... args +) +{ + std::cmatch m; + + bool match = std::regex_match(e.begin(), e.end(), m, re); + + if (match) { + detail::extract_submatches(m, std::forward(args)...); + } + + return match; +} + +template +bool +full_match( + std::basic_string_view e, + std::basic_string_view pat, + Args&&... args +) +{ + std::basic_regex re(pat.data(), pat.size()); + + return full_match(e, re, std::forward(args)...); +} + +template +bool +full_match(const std::basic_string& e, Args&&... args) +{ + return + full_match( + std::string_view{ e.data(), e.size() }, + std::forward(args)... + ); +} + +template +bool +partial_match( + std::basic_string_view e, + std::basic_string_view pat, + Args&&... args +) +{ + std::cmatch m; + std::regex re(pat.data(), pat.size()); + + bool match = std::regex_search(e.begin(), e.end(), m, re); + + if (match) { + detail::extract_submatches(m, std::forward(args)...); + } + + return match; +} + +} diff --git a/cpp/include/gherkin/rule_type.hpp b/cpp/include/gherkin/rule_type.hpp new file mode 100644 index 000000000..2a56400c3 --- /dev/null +++ b/cpp/include/gherkin/rule_type.hpp @@ -0,0 +1,59 @@ +// This file is generated. Do not edit! Edit gherkin-cpp-rule-type.razor instead. +#pragma once + +#include +#include + +namespace gherkin { + +enum class rule_type +{ + none = 0, + e_o_f, + empty, + comment, + tag_line, + feature_line, + rule_line, + background_line, + scenario_line, + examples_line, + step_line, + doc_string_separator, + table_row, + language, + other, + gherkin_document, + feature, + feature_header, + rule, + rule_header, + background, + scenario_definition, + scenario, + examples_definition, + examples, + examples_table, + step, + step_arg, + data_table, + doc_string, + tags, + description_helper, + description, + count +}; + +std::string_view +to_string(rule_type r); + +inline +std::ostream& +operator<<(std::ostream& os, rule_type r) +{ + os << to_string(r); + + return os; +} + +} diff --git a/cpp/include/gherkin/sink.hpp b/cpp/include/gherkin/sink.hpp new file mode 100644 index 000000000..fff214e0e --- /dev/null +++ b/cpp/include/gherkin/sink.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace gherkin { + +using json = nlohmann::json; + +using sink_cb = std::function; + +} diff --git a/cpp/include/gherkin/token.hpp b/cpp/include/gherkin/token.hpp new file mode 100644 index 000000000..99e156776 --- /dev/null +++ b/cpp/include/gherkin/token.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace gherkin { + +namespace cms = cucumber::messages; + +struct token +{ + bool eof = false; + gherkin::line line; + rule_type matched_type; + std::optional matched_keyword; + std::optional matched_keyword_type; + std::size_t matched_indent = 0; + gherkin::items matched_items; + std::string matched_text; + std::string matched_gherkin_dialect; + cms::location location; + + bool is_eof() const; + + void detach(); + + std::string_view value() const; +}; + +using tokens = std::vector; +using token_queue = std::deque; + +} diff --git a/cpp/include/gherkin/token_formatter_builder.hpp b/cpp/include/gherkin/token_formatter_builder.hpp new file mode 100644 index 000000000..db1c50cf8 --- /dev/null +++ b/cpp/include/gherkin/token_formatter_builder.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include +#include +#include +#include + +namespace gherkin { + +namespace cms = cucumber::messages; + +using table_rows = std::vector; +using table_cells = std::vector; +using tags = std::vector; +using comments = std::vector; + +class token_formatter_builder +{ +public: + using result_type = strings; + + token_formatter_builder(id_generator_ptr idp = {}); + virtual ~token_formatter_builder(); + + void reset(std::string_view uri); + + void start_rule(rule_type rule_type); + void end_rule(rule_type rule_type); + void build(const token& token); + + strings get_result() const; + +private: + std::string format_token(const token& token); + + id_generator_ptr idp_; + strings formatted_tokens_; +}; + +} diff --git a/cpp/include/gherkin/token_matcher.hpp b/cpp/include/gherkin/token_matcher.hpp new file mode 100644 index 000000000..8421d91ec --- /dev/null +++ b/cpp/include/gherkin/token_matcher.hpp @@ -0,0 +1,87 @@ +#pragma once + +#include +#include + +#include + +#include +#include +#include +#include + +namespace gherkin { + +using keyword_types = std::vector; +using keyword_types_map = std::unordered_map; + +class token_matcher +{ +public: + token_matcher(const std::string& dialect_name = "en"); + virtual ~token_matcher(); + + void reset(); + + bool match_feature_line(token& token); + bool match_rule_line(token& token); + bool match_scenario_line(token& token); + bool match_background_line(token& token); + bool match_examples_line(token& token); + bool match_language(token& token); + bool match_tag_line(token& token); + bool match_e_o_f(token& token); + bool match_empty(token& token); + bool match_comment(token& token); + bool match_other(token& token); + bool match_step_line(token& token); + bool match_doc_string_separator(token& token); + bool match_table_row(token& token); + +private: + bool match_doc_string_separator_( + token& token, + std::string_view separator, + bool is_open + ); + + bool match_title_line( + token& token, + rule_type token_type, + string_views keywords + ); + + struct token_info + { + std::optional text; + std::optional keyword; + std::optional keyword_type; + std::optional indent; + gherkin::items items; + }; + + void set_token_matched( + token& token, + rule_type matched_type, + const token_info& ti = {} + ); + + const string_views& keywords(std::string_view kw) const; + + cucumber::messages::step_keyword_type + keyword_type(std::string_view keyword) const; + + void change_dialect( + const std::string& dialect_name, + const cms::location& location = { 1, 1 } + ); + + std::string unescape_docstring(const std::string& text) const; + + std::string dialect_name_; + keyword_types_map keyword_types_; + std::string active_doc_string_separator_; + std::size_t indent_to_remove_; +}; + +} diff --git a/cpp/include/gherkin/token_scanner.hpp b/cpp/include/gherkin/token_scanner.hpp new file mode 100644 index 000000000..514f0fa1d --- /dev/null +++ b/cpp/include/gherkin/token_scanner.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace gherkin { + +struct next_line_result +{ + bool eof = true; + std::string text; +}; + +class token_scanner +{ +public: + token_scanner(); + token_scanner(std::string_view data); + token_scanner(const file& file); + + virtual ~token_scanner(); + + void reset(); + void reset(std::string_view data); + void reset(const file& file); + + token read(); + +private: + next_line_result next_line(); + + std::istream& input(); + + using input_ptr = std::unique_ptr; + + std::size_t line_ = 0; + input_ptr ip_; +}; + +using token_scanner_ptr = std::unique_ptr; + +template +token_scanner_ptr +new_token_scanner(Args&&... args) +{ return std::make_unique(std::forward(args)...); } + +} diff --git a/cpp/include/gherkin/type_traits.hpp b/cpp/include/gherkin/type_traits.hpp new file mode 100644 index 000000000..34bf5667d --- /dev/null +++ b/cpp/include/gherkin/type_traits.hpp @@ -0,0 +1,38 @@ +#include + +namespace gherkin { + +namespace detail { + +template < + template class Container, + template class Other, + typename T +> +std::is_same, Other> +test_is_container(Other*); + +template < + template class Container, + typename T +> +std::false_type test_is_container(T*); + +} // namespace detail + +template < + template class C, + typename T +> +using is_container = decltype( + detail::test_is_container(static_cast(nullptr)) +); + +template < + template class C, + typename T +> +inline +constexpr bool is_container_v = is_container::value; + +} diff --git a/cpp/include/gherkin/types.hpp b/cpp/include/gherkin/types.hpp new file mode 100644 index 000000000..c6d975fa5 --- /dev/null +++ b/cpp/include/gherkin/types.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace gherkin { + +using strings = std::vector; +using strings_map = std::unordered_map; +using string_set = std::set; +using string_set_map = std::unordered_map; +using string_map = std::unordered_map; + +using string_views = std::vector; +using string_view_set = std::set; + +} diff --git a/cpp/include/gherkin/utils.hpp b/cpp/include/gherkin/utils.hpp new file mode 100644 index 000000000..a9c53e492 --- /dev/null +++ b/cpp/include/gherkin/utils.hpp @@ -0,0 +1,257 @@ +#pragma once + +#include +#include +#include +#include + +namespace gherkin { + +// utility wrapper to adapt locale-bound facets for wstring/wbuffer convert +template +struct deletable_facet : Facet +{ + template + deletable_facet(Args&&... args) + : Facet(std::forward(args)...) + {} + + ~deletable_facet() + {} +}; + +inline +std::wstring +to_wide(const std::string& s) +{ + std::wstring_convert,char32_t> cv; + + auto ws = cv.from_bytes(s); + + return {ws.begin(), ws.end()}; +} + +inline +std::string +to_narrow(const std::wstring& ws) +{ + std::wstring_convert,char32_t> cv; + + std::u32string s{ws.begin(), ws.end()}; + + return cv.to_bytes(s); +} + +enum class re_pattern +{ + none, + all_spaces, + spaces_no_nl, + crlf, + cr, + bol, + eol +}; + +template +struct re_patterns +{ + static + auto + convert(std::string_view sv) + { + if constexpr (sizeof(CharT) > sizeof(char)) { + return to_wide(std::string(sv)); + } else { + return std::string(sv); + } + } + + static + auto get(re_pattern p) + { + using namespace std::literals; + std::string_view sv; + + switch (p) { + case re_pattern::none: + break; + case re_pattern::all_spaces: + sv = "[ \\t\\n\\v\\f\\r\\u0085\\u00A0]+"sv; + break; + case re_pattern::spaces_no_nl: + sv = "[ \\t\\v\\f\\r\\u0085\\u00A0]+"sv; + break; + case re_pattern::crlf: + sv = "\\r\\n"sv; + case re_pattern::cr: + sv = "\\r"sv; + break; + case re_pattern::bol: + sv = "^"sv; + break; + case re_pattern::eol: + sv = "$"sv; + break; + } + + return convert(sv); + } +}; + +template +struct strip_pattern +{ + using s_type = std::basic_string; + using sv_type = std::basic_string_view; + using pats = re_patterns; + + strip_pattern(re_pattern prefix, sv_type chars) + : strip_pattern(prefix, chars, re_pattern::none) + {} + + strip_pattern(sv_type chars, re_pattern suffix) + : strip_pattern(re_pattern::none, chars, suffix) + {} + + strip_pattern(sv_type chars) + : strip_pattern(re_pattern::none, chars, re_pattern::none) + {} + + strip_pattern(re_pattern prefix, sv_type chars, re_pattern suffix) + { + if (prefix != re_pattern::none) { + s_ = pats::get(prefix); + } + + s_ += chars; + + if (suffix != re_pattern::none) { + s_ += pats::get(suffix); + } + } + + const s_type& str() const + { return s_; } + + s_type s_; +}; + +template +std::basic_string_view +as_view(const std::basic_string& s) +{ return { s.data(), s.size() }; } + +std::size_t +codepoint_count(std::string_view s); + +std::string +slurp(const std::string& path); + +void +replace(std::string& s, std::string_view what, std::string_view with); + +std::string +replace(const std::string& s, std::string_view what, std::string_view with); + +template +std::basic_string +strip(std::basic_string_view what, const strip_pattern& p) +{ + std::basic_regex re(p.str()); + std::basic_string empty; + std::basic_string result; + + std::regex_replace( + std::back_inserter(result), + what.begin(), what.end(), + re, + empty + ); + + return result; +} + +template +std::basic_string +lstrip( + std::basic_string_view in, + re_pattern p = re_pattern::all_spaces +) +{ + return + strip( + in, + strip_pattern( + re_pattern::bol, + re_patterns::get(p) + ) + ); +} + +template +std::basic_string +lstrip( + const std::basic_string& in, + re_pattern p = re_pattern::all_spaces +) +{ return lstrip(as_view(in), p); } + +template +std::basic_string +rstrip( + std::basic_string_view in, + re_pattern p = re_pattern::all_spaces +) +{ + return + strip( + in, + strip_pattern( + re_patterns::get(p), + re_pattern::eol + ) + ); +} + +template +std::basic_string +rstrip( + const std::basic_string& in, + re_pattern p = re_pattern::all_spaces +) +{ return rstrip(as_view(in), p); } + +template +std::basic_string +strip( + std::basic_string_view in, + re_pattern p = re_pattern::all_spaces +) +{ return lstrip(rstrip(in, p), p); } + +template +std::basic_string +strip( + const std::basic_string& in, + re_pattern p = re_pattern::all_spaces +) +{ return strip(as_view(in), p); } + +template +struct reverse +{ + reverse(C& c) + : c_(c) + {} + + auto begin() + { return c_.rbegin(); } + + auto end() + { return c_.rend(); } + + C& c_; +}; + +} diff --git a/cpp/scripts/build-ext b/cpp/scripts/build-ext new file mode 100755 index 000000000..a8c64fa11 --- /dev/null +++ b/cpp/scripts/build-ext @@ -0,0 +1,123 @@ +#!/usr/bin/env bash + +set -e + +ME=$(basename $0) +MYDIR=$(cd $(dirname $0)/.. && pwd) + +############################################################################### +# +# Minimal checks +# +############################################################################### +[ -z "$1" ] && echo "$ME: missing directory" && exit 1 +[ ! -d "$1" ] && echo "$ME: invalid directory: $1" && exit 1 + +SRCDIR="$1" +shift + +while ((${#1})); do + case "$1" in + --src-dir=*) + SUBDIR="${1##--src-dir=}" + SRCDIR="$SRCDIR/$SUBDIR" + shift + ;; + *) + break + ;; + esac +done + +############################################################################### +# +# Compiler setup +# +############################################################################### +BUILDROOT=$MYDIR/build/root +CC= +CFLAGS= +CXX= +CXXFLAGS=$CFLAGS +CPATH=$BUILDROOT/include +LIBRARY_PATH=$BUILDROOT/lib +LD_LIBRARY_PATH=$BUILDROOT/lib +PKG_CONFIG_PATH=$LIBRARY_PATH/pkgconfig + +#CCACHE=$(which ccache || true) + +# if [ -n "$CCACHE" ]; then +# CC="$CCACHE $CC" +# CXX="$CCACHE $CXX" +# fi + +export CC CFLAGS CXX CXXFLAGS CPATH LIBRARY_PATH LD_LIBRARY_PATH + +############################################################################### +# +# Parallel compilation setup +# +############################################################################### +MAKEARGS= +CMAKEARGS= + +if [ -n "$NPROCS" ]; then + CMAKEARGS="--parallel $NPROCS" + MAKEARGS="-j$NPROCS" +else + CMAKEARGS="--parallel" +fi + +############################################################################### +# +# Project build +# +############################################################################### +PROJECT=$(basename $SRCDIR) +BUILDDIR=$MYDIR/build/$PROJECT + +mkdir -p $LIBRARY_PATH +rm -rf $BUILDDIR && mkdir -p $BUILDDIR + +if [ -f $SRCDIR/CMakeLists.txt ]; then + cmake \ + -G Ninja \ + -DCMAKE_PREFIX_PATH=$BUILDROOT \ + -DCMAKE_INSTALL_PREFIX=$BUILDROOT \ + -DCMAKE_TOOLCHAIN_FILE=$MYDIR/cmake/toolchains/ext.cmake \ + -S $SRCDIR -B $BUILDDIR \ + "$@" + cmake --build $BUILDDIR $CMAKEARGS + cmake --install $BUILDDIR +elif [ -f $SRCDIR/meson.build ]; then + cd $BUILDDIR + meson \ + --prefix=$BUILDROOT \ + --pkg-config-path=$BUILDROOT \ + --cmake-prefix-path=$BUILDROOT \ + "$@" \ + . $SRCDIR + meson install +elif [ -x $SRCDIR/configure ]; then + cd $BUILDDIR + $SRCDIR/configure \ + --prefix=$BUILDROOT \ + --enable-shared \ + "$@" + make $MAKEARGS + make install +elif [ -x $SRCDIR/config ]; then + cd $BUILDDIR + $SRCDIR/config --prefix=$BUILDROOT "$@" + make $MAKEARGS + make install +elif [ -f $SRCDIR/Makefile ]; then + cd $SRCDIR + make $MAKEARGS + make prefix=$BUILDROOT install +else + echo "$ME: don't know how to build $SRCDIR" + exit 1 +fi + +rm -rf $BUILDDIR diff --git a/cpp/scripts/build-externals b/cpp/scripts/build-externals new file mode 100755 index 000000000..daea781b2 --- /dev/null +++ b/cpp/scripts/build-externals @@ -0,0 +1,172 @@ +#!/usr/bin/env bash + +############################################################################### +# +# Barebones dependency installer +# +############################################################################### +set -e +trap "exit 1" TERM + +ME=$(basename $0) +MYDIR=$(cd $(dirname $0)/.. && pwd) +MYPID=$$ + +BUILDER=$MYDIR/scripts/build-ext + +EXTDIR=$MYDIR/build/ext +DLFILE=$EXTDIR/.downloaded +INSTFILE=$EXTDIR/.installed +TMPDIR=$EXTDIR/tmp +ARCDIR=$EXTDIR/archives + +function error() { + echo "$@" >&2 + kill -TERM $MYPID + exit 1 +} + +function clean_tmp() { + rm -rf $TMPDIR && mkdir -p $TMPDIR +} + +function get_unique() { + local WHERE=$1 + local TYPE=$2 + + local COUNT=$(find $WHERE -mindepth 1 -maxdepth 1 -type $TYPE | wc -l) + + [ "$COUNT" -ne "1" ] && error "no unique entry of type $TYPE in $WHERE" + + local ENTRY=$(find $WHERE -mindepth 1 -maxdepth 1 -type $TYPE) + + echo $ENTRY +} + +function get_md5() { + local WHAT=$1 + local MD5 + + case $(uname -s) in + Darwin) + MD5=$(md5 -qs "$WHAT") + ;; + *) + MD5=$(echo -n "$WHAT" | md5sum | cut -d ' ' -f 1) + ;; + esac + + echo $MD5 +} + +function download() { + local PKG=$1 + + local MD5=$(get_md5 "$PKG") + local ARCHIVE=$(grep $MD5 $DLFILE | cut -d ' ' -f 2) + + if [ -z "$ARCHIVE" ]; then + clean_tmp + + (cd $TMPDIR && curl -LJO $PKG) + + mkdir -p $ARCDIR + + ARCHIVE=$(get_unique $TMPDIR f) + + mv $ARCHIVE $ARCDIR + + ARCHIVE=$(basename $ARCHIVE) + echo "$MD5 $ARCHIVE" >> $DLFILE + fi + + echo $ARCHIVE +} + +function extract() { + local ARCHIVE=$1 + + local ARCFILE=$ARCDIR/$ARCHIVE + local TAROPTS + + case $(uname -s) in + Darwin) + TAROPTS=xf + ;; + *) + TAROPTS=xaf + ;; + esac + + clean_tmp + + if [[ "$ARCFILE" =~ \.zip$ ]]; then + (cd $TMPDIR && unzip -q $ARCFILE) + elif [[ "$ARCFILE" =~ \.tar\.(gz|bz2|xz)$ ]]; then + (cd $TMPDIR && tar $TAROPTS $ARCFILE) + else + error "unhandled archive format $ARCHIVE" + fi + + DIR=$(get_unique $TMPDIR d) + + echo $DIR +} + +function build() { + local URL=$1 + shift + + local ARCHIVE=$(download $URL) + local INSTALLED=$(grep $ARCHIVE $INSTFILE) + + if [ -z "$INSTALLED" ]; then + local DEPDIR=$(extract $ARCHIVE) + + echo "building from $URL" + + $BUILDER $DEPDIR "$@" + + echo $ARCHIVE >> $INSTFILE + else + echo "skipping already installed $ARCHIVE from $URL" + fi +} + +function build_list() { + local EXTFILE=$1 + local -a SPEC=() + + while read LINE; do + [[ "$LINE" =~ ^([[:space:]]*(#.*)?)?$ ]] && continue + + build $LINE + done < $EXTFILE +} + +mkdir -p $EXTDIR +touch $DLFILE $INSTFILE + +[ -z "$1" ] && echo "usage: $ME FILE|URL" && exit 1 + +WHAT=$1 +shift + +if [ -f $WHAT ]; then + # Assume a dependency list file + if [ -n "$1" ]; then + # Short keyword to easily select something in the list file + LINE=$(grep "$1" $WHAT) + + if [ -n "$LINE" ]; then + build $LINE + fi + else + build_list $WHAT + fi +else + # Assume a URL + build $WHAT "$@" +fi + +exit 0 diff --git a/cpp/scripts/nddiff.sh b/cpp/scripts/nddiff.sh new file mode 100755 index 000000000..1bbbb17be --- /dev/null +++ b/cpp/scripts/nddiff.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +F=$1 +N=$(basename $F) + +stage/bin/gherkin \ + --no-source --no-pickles \ + $F \ + | jq \ + --sort-keys --compact-output "." \ + > acceptance/testdata/good/$N.ast.ndjson + +meld \ + <(jq "." $F.ast.ndjson) \ + <(jq "." acceptance/testdata/good/$N.ast.ndjson) diff --git a/cpp/src/bin/gherkin-generate-tokens/CMakeLists.txt b/cpp/src/bin/gherkin-generate-tokens/CMakeLists.txt new file mode 100644 index 000000000..8060f2dcb --- /dev/null +++ b/cpp/src/bin/gherkin-generate-tokens/CMakeLists.txt @@ -0,0 +1,26 @@ +add_executable(gherkin-generate-tokens-bin) + +target_sources( + gherkin-generate-tokens-bin + PRIVATE + ${CMAKE_SOURCE_DIR}/src/bin/gherkin-generate-tokens/gherkin-generate-tokens.cpp +) + +target_include_directories( + gherkin-generate-tokens-bin + PRIVATE + ${CMAKE_SOURCE_DIR}/ + ${CMAKE_SOURCE_DIR}/src/bin/gherkin-generate-tokens +) + +target_link_libraries( + gherkin-generate-tokens-bin + PUBLIC + gherkin-cpp +) + +set_target_properties( + gherkin-generate-tokens-bin + PROPERTIES + OUTPUT_NAME gherkin-generate-tokens +) diff --git a/cpp/src/bin/gherkin-generate-tokens/gherkin-generate-tokens.cpp b/cpp/src/bin/gherkin-generate-tokens/gherkin-generate-tokens.cpp new file mode 100644 index 000000000..3c265c953 --- /dev/null +++ b/cpp/src/bin/gherkin-generate-tokens/gherkin-generate-tokens.cpp @@ -0,0 +1,29 @@ +#include +#include + +#include +#include +#include +#include + +int main(int ac, char** av) +{ + using builder = gherkin::token_formatter_builder; + using parser = gherkin::parser; + + parser p; + + for (std::size_t i = 1; i < ac; ++i) { + std::string file(av[i]); + + auto data = gherkin::slurp(file); + + auto ss = p.parse(file, data); + + std::cout << gherkin::join("\n", ss); + } + + std::cout << std::endl; + + return 0; +} diff --git a/cpp/src/bin/gherkin/CMakeLists.txt b/cpp/src/bin/gherkin/CMakeLists.txt new file mode 100644 index 000000000..a20251a90 --- /dev/null +++ b/cpp/src/bin/gherkin/CMakeLists.txt @@ -0,0 +1,26 @@ +add_executable(gherkin-bin) + +target_sources( + gherkin-bin + PRIVATE + ${CMAKE_SOURCE_DIR}/src/bin/gherkin/gherkin.cpp +) + +target_include_directories( + gherkin-bin + PRIVATE + ${CMAKE_SOURCE_DIR}/ + ${CMAKE_SOURCE_DIR}/src/bin/gherkin +) + +target_link_libraries( + gherkin-bin + PUBLIC + gherkin-cpp +) + +set_target_properties( + gherkin-bin + PROPERTIES + OUTPUT_NAME gherkin +) diff --git a/cpp/src/bin/gherkin/gherkin.cpp b/cpp/src/bin/gherkin/gherkin.cpp new file mode 100644 index 000000000..57d95d410 --- /dev/null +++ b/cpp/src/bin/gherkin/gherkin.cpp @@ -0,0 +1,92 @@ +#include + +#include +#include + +struct options +{ + bool exit = false; + int exit_code = 0; + int last_arg = 0; + bool include_source = true; + bool include_ast = true; + bool include_pickles = true; + bool predicatable_ids = true; +}; + +options +parse_options(int ac, char** av) +{ + options opts; + + for (opts.last_arg = 1; opts.last_arg < ac; ++opts.last_arg) { + std::string_view arg(av[opts.last_arg]); + + if (arg == "--no-source") { + opts.include_source = false; + } else if (arg == "--no-ast") { + opts.include_ast = false; + } else if (arg == "--no-pickles") { + opts.include_pickles = false; + } else if (arg == "--predictable-ids") { + opts.predicatable_ids = true; + } else if (arg.find('-') == 0) { + if (arg != "-h" && arg != "--help") { + std::cout << "Unknown option: " << arg << std::endl; + opts.exit_code = 1; + } + + std::cout + << "Usage: gherkin [options] FILE...\n" + << " -h, --help You're looking at it.\n" + << " --no-ast Do not emit Ast events.\n" + << " --no-pickles Do not emit Pickle events.\n" + << " --no-source Do not emit Source events." + << std::endl + ; + + opts.exit = true; + } else { + break; + } + } + + return opts; +} + +template +void print_json_obj(std::string_view key, const Obj& o) +{ + nlohmann::json j; + + o.to_json(j[key]); + + std::cout << j << std::endl; +} + +int main(int ac, char** av) +{ + auto opts = parse_options(ac, av); + + if (opts.exit) { + return opts.exit_code; + } + + gherkin::app app; + gherkin::app::callbacks cbs{ + .source = [&](const auto& m) { print_json_obj("source", m); }, + .ast = [&](const auto& m) { print_json_obj("gherkinDocument", m); }, + .pickle = [&](const auto& m) { print_json_obj("pickle", m); }, + .error = [&](const auto& m) { std::cout << m.to_json() << std::endl; } + }; + + app.include_source(opts.include_source); + app.include_ast(opts.include_ast); + app.include_pickles(opts.include_pickles); + + for ( ; opts.last_arg < ac; ++opts.last_arg) { + app.parse(gherkin::file{ av[opts.last_arg] }, cbs); + } + + return 0; +} diff --git a/cpp/src/lib/gherkin/CMakeLists.txt b/cpp/src/lib/gherkin/CMakeLists.txt new file mode 100644 index 000000000..fda4c0584 --- /dev/null +++ b/cpp/src/lib/gherkin/CMakeLists.txt @@ -0,0 +1,38 @@ +add_library(gherkin-cpp) +add_library(gherkin::gherkin-cpp ALIAS gherkin-cpp) + +set(INC_DIR "${CMAKE_SOURCE_DIR}/include") + +# We prefer it that way... +file(GLOB_RECURSE GHERKIN_CPP_HEADERS ${INC_DIR}/*.[ch]pp) +file(GLOB_RECURSE GHERKIN_CPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.[ch]pp) + +target_sources( + gherkin-cpp + PRIVATE + ${GHERKIN_CPP_HEADERS} + ${GHERKIN_CPP_SOURCES} +) + +target_include_directories( + gherkin-cpp + PUBLIC + $ + $ + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_libraries( + gherkin-cpp + PUBLIC + cucumber::cucumber-messages + nlohmann_json::nlohmann_json +) + +set_target_properties( + gherkin-cpp + PROPERTIES + OUTPUT_NAME gherkin-cpp +) + diff --git a/cpp/src/lib/gherkin/app.cpp b/cpp/src/lib/gherkin/app.cpp new file mode 100644 index 000000000..8b351644b --- /dev/null +++ b/cpp/src/lib/gherkin/app.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include + +namespace gherkin { + +app::app() +: idp_(new_id_generator()), +p_(idp_) +{} + +app::~app() +{} + +void +app::include_source(bool f) +{ include_source_ = f; } + +void +app::include_ast(bool f) +{ include_ast_ = f; } + +void +app::include_pickles(bool f) +{ include_pickles_ = f; } + +void +app::parse(const file& f, const callbacks& cbs) +{ + cms::envelope e; + + e.source = cms::source{ .uri = f.path, .data = slurp(f.path) }; + + parse(e, cbs); +} + +void +app::parse(const cms::envelope& e, const callbacks& cbs) +{ + if (include_source_ && cbs.source) { + cbs.source(*e.source); + } + + parse(*e.source, cbs); +} + +void +app::parse(const cms::source& s, const callbacks& cbs) +{ + try { + auto ast = p_.parse(s.uri, s.data); + + if (include_ast_ && cbs.ast) { + cbs.ast(ast); + } + + if (include_pickles_ && cbs.pickle) { + pickle_compiler pc(idp_); + + pc.compile(ast, s.uri, cbs.pickle); + } + } catch (const composite_parser_error& e) { + for (const auto& ep : e.errors()) { + send_parse_error(s.uri, *ep, cbs); + } + } catch (const parser_error& e) { + send_parse_error(s.uri, e, cbs); + } +} + +void +app::send_parse_error( + const std::string& uri, + const parser_error& e, + const callbacks& cbs +) const +{ + parse_error pe{ + .uri = uri, + .location = e.location(), + .message = e.what() + }; + + call_cb(cbs.error, pe); +} + +} diff --git a/cpp/src/lib/gherkin/ast_builder.cpp b/cpp/src/lib/gherkin/ast_builder.cpp new file mode 100644 index 000000000..84d473af3 --- /dev/null +++ b/cpp/src/lib/gherkin/ast_builder.cpp @@ -0,0 +1,478 @@ +#include + +#include + +#include +#include +#include +#include + +namespace gherkin { + +ast_builder::ast_builder() +: ast_builder(new_id_generator()) +{} + +ast_builder::ast_builder(id_generator_ptr idp) +: idp_(idp) +{} + +ast_builder::~ast_builder() +{} + +void +ast_builder::reset(std::string_view uri) +{ + stack_ = {}; + stack_.emplace(rule_type::none); + comments_.clear(); + uri_ = uri; +} + +void +ast_builder::start_rule(rule_type rule_type) +{ stack_.emplace(rule_type); } + +void +ast_builder::end_rule(rule_type rule_type) +{ + auto n = pop_node(); + auto rt = n.type(); + + transform_node(n, current_node()); +} + +void +ast_builder::build(const token& token) +{ + if (token.matched_type == rule_type::comment) { + cms::comment c{ + .location = get_location(token), + .text = token.matched_text + }; + + comments_.emplace_back(std::move(c)); + } else { + current_node().add(token.matched_type, token); + } +} + +const cms::gherkin_document& +ast_builder::get_result() const +{ + auto dp = current_node().get_single( + rule_type::gherkin_document + ); + + return dp ? *dp : doc_; +}; + +std::string +ast_builder::next_id() +{ return idp_->next_id(); } + +void +ast_builder::transform_node(ast_node& from, ast_node& to) +{ + if (from.is(rule_type::step)) { + to.add(from.type(), make_step(from)); + } else if (from.is(rule_type::doc_string)) { + to.add(from.type(), make_doc_string(from)); + } else if (from.is(rule_type::data_table)) { + to.add(from.type(), make_data_table(from)); + } else if (from.is(rule_type::background)) { + to.add(from.type(), make_background(from)); + } else if (from.is(rule_type::scenario_definition)) { + to.add(from.type(), make_scenario_definition(from)); + } else if (from.is(rule_type::examples_definition)) { + to.add(from.type(), make_examples_definition(from)); + } else if (from.is(rule_type::examples_table)) { + to.add(from.type(), make_examples_table(from)); + } else if (from.is(rule_type::description)) { + to.add(from.type(), make_description(from)); + } else if (from.is(rule_type::feature)) { + to.add(from.type(), make_feature(from)); + } else if (from.is(rule_type::rule)) { + to.add(from.type(), make_rule(from)); + } else if (from.is(rule_type::gherkin_document)) { + to.add(from.type(), make_gherkin_document(from)); + } else { + to.add(from.type(), from); + } +} + +cms::step +ast_builder::make_step(ast_node& node) +{ + auto& step_line = node.get_token(rule_type::step_line); + + cms::step m{ + .location = get_location(step_line), + .keyword = step_line.matched_keyword.value_or(""), + .keyword_type = step_line.matched_keyword_type, + .text = step_line.matched_text, + .id = next_id() + }; + + node.set(rule_type::doc_string, m.doc_string); + node.set(rule_type::data_table, m.data_table); + + return m; +} + +cms::doc_string +ast_builder::make_doc_string(ast_node& node) +{ + auto& tokens = node.get_tokens(rule_type::doc_string_separator); + auto& separator_token = tokens[0]; + + string_views svs; + + for (const auto& t : node.get_tokens(rule_type::other)) { + svs.emplace_back(t.matched_text); + } + + auto content = join("\n", svs); + + cms::doc_string m{ + .location = get_location(separator_token), + .content = content, + .delimiter = separator_token.matched_keyword.value_or("") + }; + + if (!separator_token.matched_text.empty()) { + m.media_type = separator_token.matched_text; + } + + return m; +} + +cms::data_table +ast_builder::make_data_table(ast_node& node) +{ + auto rows = get_table_rows(node); + + cms::data_table m{ + .rows = std::move(rows) + }; + + if (!m.rows.empty()) { + m.location = m.rows.front().location; + } + + return m; +} + +cms::background +ast_builder::make_background(ast_node& node) +{ + auto& background_line = node.get_token(rule_type::background_line); + + cms::background m{ + .location = get_location(background_line), + .keyword = background_line.matched_keyword.value_or(""), + .name = background_line.matched_text, + .id = next_id() + }; + + node.set(rule_type::description, m.description); + node.set(rule_type::step, m.steps); + + return m; +} + +cms::scenario +ast_builder::make_scenario_definition(ast_node& node) +{ + auto pnode = node.get_single(rule_type::scenario); + auto& scenario_node = *pnode; + auto& scenario_line = scenario_node.get_token(rule_type::scenario_line); + + cms::scenario m{ + .location = get_location(scenario_line), + .tags = get_tags(node), + .keyword = scenario_line.matched_keyword.value_or(""), + .name = scenario_line.matched_text, + .id = next_id() + }; + + scenario_node.set(rule_type::description, m.description); + scenario_node.set(rule_type::step, m.steps); + scenario_node.set(rule_type::examples_definition, m.examples); + + return m; +} + +cms::examples +ast_builder::make_examples_definition(ast_node& node) +{ + auto pnode = node.get_single(rule_type::examples); + auto& examples_node = *pnode; + auto& examples_line = examples_node.get_token(rule_type::examples_line); + + cms::examples m{ + .location = get_location(examples_line), + .tags = get_tags(node), + .keyword = examples_line.matched_keyword.value_or(""), + .name = examples_line.matched_text, + .id = next_id() + }; + + examples_node.set(rule_type::description, m.description); + + auto prows = examples_node.get_single( + rule_type::examples_table + ); + + if (prows) { + auto& rows = *prows; + + m.table_header = rows.front(); + + if (rows.size() > 1) { + std::copy( + rows.begin() + 1, rows.end(), + std::back_inserter(m.table_body) + ); + } + } + + return m; +} + +table_rows +ast_builder::make_examples_table(ast_node& node) +{ return get_table_rows(node); } + +std::string +ast_builder::make_description(ast_node& node) +{ + std::regex only_spaces("\\s*"); + auto toks = node.get_tokens(rule_type::other); + std::size_t ntoks = toks.size(); + + while ( + ntoks + && + full_match(toks[ntoks - 1].matched_text, only_spaces) + ) { + --ntoks; + } + + string_views svs; + + for (std::size_t i = 0; i < ntoks; ++i) { + svs.emplace_back(toks[i].matched_text); + } + + return join("\n", svs); +} + +cms::feature +ast_builder::make_feature(ast_node& node) +{ + auto pnode = node.get_single(rule_type::feature_header); + auto& header = *pnode; + + auto ptoken = header.get_single(rule_type::feature_line); + auto& feature_line = *ptoken; + + cms::feature m{ + .location = get_location(feature_line), + .tags = get_tags(header), + .language = feature_line.matched_gherkin_dialect, + .keyword = feature_line.matched_keyword.value_or(""), + .name = feature_line.matched_text + }; + + header.set(rule_type::description, m.description); + + auto pb = node.get_single(rule_type::background); + + if (pb) { + m.children.emplace_back(cms::feature_child{ .background = *pb }); + } + + auto ps = node.get_items(rule_type::scenario_definition); + + if (ps) { + for (auto& scenario : *ps) { + m.children.emplace_back(cms::feature_child{.scenario = scenario}); + } + } + + auto pr = node.get_items(rule_type::rule); + + if (pr) { + for (auto& rule : *pr) { + m.children.emplace_back(cms::feature_child{.rule = rule}); + } + } + + return m; +} + +cms::rule +ast_builder::make_rule(ast_node& node) +{ + auto pnode = node.get_single(rule_type::rule_header); + auto& header = *pnode; + + auto ptoken = header.get_single(rule_type::rule_line); + auto& rule_line = *ptoken; + + cms::rule m{ + .location = get_location(rule_line), + .tags = get_tags(header), + .keyword = rule_line.matched_keyword.value_or(""), + .name = rule_line.matched_text, + .id = next_id() + }; + + header.set(rule_type::description, m.description); + + auto pb = node.get_single(rule_type::background); + + if (pb) { + m.children.emplace_back(cms::rule_child{ .background = *pb }); + } + + auto ps = node.get_items(rule_type::scenario_definition); + + if (ps) { + for (auto& scenario : *ps) { + m.children.emplace_back(cms::rule_child{.scenario = scenario}); + } + } + + return m; +} + +cms::gherkin_document +ast_builder::make_gherkin_document(ast_node& node) +{ + cms::gherkin_document gd{ + .uri = std::string(uri_), + .comments = comments_ + }; + + node.set(rule_type::feature, gd.feature); + + return gd; +} + +cms::location +ast_builder::get_location( + const token& token, + std::size_t column +) const +{ + cms::location m{ + .line = token.location.line, + .column = column == 0 ? token.location.column : column + }; + + return m; +} + +table_rows +ast_builder::get_table_rows(const ast_node& node) +{ + table_rows rows; + + for (const auto& token : node.get_tokens(rule_type::table_row)) { + rows.emplace_back(cms::table_row{ + .location = get_location(token), + .cells = get_table_cells(token), + .id = next_id() + }); + } + + ensure_cell_count(rows); + + return rows; +} + +void +ast_builder::ensure_cell_count(const table_rows& rows) const +{ + if (rows.empty()) { + return; + } + + std::size_t cell_count = rows.front().cells.size(); + + for (const auto& row : rows) { + if (row.cells.size() != cell_count) { + throw + ast_builder_error( + "inconsistent cell count within the table", + { + .line = row.location.line, + .column = row.location.column.value_or(0) + } + ); + } + } +} + +table_cells +ast_builder::get_table_cells(const token& token) +{ + table_cells cells; + + for (const auto& i : token.matched_items) { + cells.emplace_back(cms::table_cell{ + .location = get_location(token, i.column), + .value = i.text + }); + } + + return cells; +} + +tags +ast_builder::get_tags(const ast_node& node) +{ + tags ts; + + auto pnode = node.get_single(rule_type::tags); + + if (pnode) { + auto& tags_node = *pnode; + + for (const auto& token : tags_node.get_tokens(rule_type::tag_line)) { + for (auto& tag_item : token.matched_items) { + cms::tag t{ + .location = get_location(token, tag_item.column), + .name = tag_item.text, + .id = next_id() + }; + + ts.emplace_back(std::move(t)); + } + } + } + + return ts; +} + +ast_node +ast_builder::pop_node() +{ + ast_node n = std::move(current_node()); + + stack_.pop(); + + return n; +} + +ast_node& +ast_builder::current_node() +{ return stack_.top(); } + +const ast_node& +ast_builder::current_node() const +{ return stack_.top(); } + +} diff --git a/cpp/src/lib/gherkin/ast_node.cpp b/cpp/src/lib/gherkin/ast_node.cpp new file mode 100644 index 000000000..4511958d8 --- /dev/null +++ b/cpp/src/lib/gherkin/ast_node.cpp @@ -0,0 +1,48 @@ +#include + +namespace gherkin { + +ast_node::ast_node(rule_type rule_type) +: rule_type_(rule_type) +{} + +ast_node::ast_node(const ast_node& other) +: rule_type_(other.rule_type_), +sub_items_(other.sub_items_) +{} + +ast_node::ast_node(ast_node&& other) +: rule_type_(std::move(other.rule_type_)), +sub_items_(std::move(other.sub_items_)) +{} + +ast_node::~ast_node() +{} + +ast_node& +ast_node::operator=(const ast_node& other) +{ + rule_type_ = other.rule_type_; + sub_items_ = other.sub_items_; + + return *this; +} + +ast_node& +ast_node::operator=(ast_node&& other) +{ + rule_type_ = std::move(other.rule_type_); + sub_items_ = std::move(other.sub_items_); + + return *this; +} + +bool +ast_node::is(rule_type rule_type) const +{ return rule_type_ == rule_type; } + +rule_type +ast_node::type() const +{ return rule_type_; } + +} diff --git a/cpp/src/lib/gherkin/demangle.cpp b/cpp/src/lib/gherkin/demangle.cpp new file mode 100644 index 000000000..c87ff22bc --- /dev/null +++ b/cpp/src/lib/gherkin/demangle.cpp @@ -0,0 +1,27 @@ +// https://gist.github.com/bwoods/bbc6bd26b73fa37e94ac + +#include // gcc and clang… +#include + +#include + +namespace gherkin { + +namespace detail { + +auto +demangle(std::string&& name) +-> std::unique_ptr +{ + int status = 0; + + return + { + abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), + [] (char * p) { ::free(p); } + }; +} + +} // namespace detail + +} diff --git a/cpp/src/lib/gherkin/dialect.cpp b/cpp/src/lib/gherkin/dialect.cpp new file mode 100644 index 000000000..89abfb5b5 --- /dev/null +++ b/cpp/src/lib/gherkin/dialect.cpp @@ -0,0 +1,1284 @@ +#include + +namespace gherkin { + +const keywords_maps& +all_keywords() +{ + static const keywords_maps kwms = { + { + "af", + { + { "and", { "* ", "En " } }, + { "background", { "Agtergrond" } }, + { "but", { "* ", "Maar " } }, + { "examples", { "Voorbeelde" } }, + { "feature", { "Funksie", "Besigheid Behoefte", "Vermoë" } }, + { "given", { "* ", "Gegewe " } }, + { "rule", { "Regel" } }, + { "scenario", { "Voorbeeld", "Situasie" } }, + { "scenarioOutline", { "Situasie Uiteensetting" } }, + { "then", { "* ", "Dan " } }, + { "when", { "* ", "Wanneer " } } + } + }, + { + "am", + { + { "and", { "* ", "Եվ " } }, + { "background", { "Կոնտեքստ" } }, + { "but", { "* ", "Բայց " } }, + { "examples", { "Օրինակներ" } }, + { "feature", { "Ֆունկցիոնալություն", "Հատկություն" } }, + { "given", { "* ", "Դիցուք " } }, + { "rule", { "Rule" } }, + { "scenario", { "Օրինակ", "Սցենար" } }, + { "scenarioOutline", { "Սցենարի կառուցվացքը" } }, + { "then", { "* ", "Ապա " } }, + { "when", { "* ", "Եթե ", "Երբ " } } + } + }, + { + "an", + { + { "and", { "* ", "Y ", "E " } }, + { "background", { "Antecedents" } }, + { "but", { "* ", "Pero " } }, + { "examples", { "Eixemplos" } }, + { "feature", { "Caracteristica" } }, + { "given", { "* ", "Dau ", "Dada ", "Daus ", "Dadas " } }, + { "rule", { "Rule" } }, + { "scenario", { "Eixemplo", "Caso" } }, + { "scenarioOutline", { "Esquema del caso" } }, + { "then", { "* ", "Alavez ", "Allora ", "Antonces " } }, + { "when", { "* ", "Cuan " } } + } + }, + { + "ar", + { + { "and", { "* ", "و " } }, + { "background", { "الخلفية" } }, + { "but", { "* ", "لكن " } }, + { "examples", { "امثلة" } }, + { "feature", { "خاصية" } }, + { "given", { "* ", "بفرض " } }, + { "rule", { "Rule" } }, + { "scenario", { "مثال", "سيناريو" } }, + { "scenarioOutline", { "سيناريو مخطط" } }, + { "then", { "* ", "اذاً ", "ثم " } }, + { "when", { "* ", "متى ", "عندما " } } + } + }, + { + "ast", + { + { "and", { "* ", "Y ", "Ya " } }, + { "background", { "Antecedentes" } }, + { "but", { "* ", "Peru " } }, + { "examples", { "Exemplos" } }, + { "feature", { "Carauterística" } }, + { "given", { "* ", "Dáu ", "Dada ", "Daos ", "Daes " } }, + { "rule", { "Rule" } }, + { "scenario", { "Exemplo", "Casu" } }, + { "scenarioOutline", { "Esbozu del casu" } }, + { "then", { "* ", "Entós " } }, + { "when", { "* ", "Cuando " } } + } + }, + { + "az", + { + { "and", { "* ", "Və ", "Həm " } }, + { "background", { "Keçmiş", "Kontekst" } }, + { "but", { "* ", "Amma ", "Ancaq " } }, + { "examples", { "Nümunələr" } }, + { "feature", { "Özəllik" } }, + { "given", { "* ", "Tutaq ki ", "Verilir " } }, + { "rule", { "Rule" } }, + { "scenario", { "Nümunə", "Ssenari" } }, + { "scenarioOutline", { "Ssenarinin strukturu" } }, + { "then", { "* ", "O halda " } }, + { "when", { "* ", "Əgər ", "Nə vaxt ki " } } + } + }, + { + "be", + { + { "and", { "* ", "I ", "Ды ", "Таксама " } }, + { "background", { "Кантэкст" } }, + { "but", { "* ", "Але ", "Інакш " } }, + { "examples", { "Прыклады" } }, + { "feature", { "Функцыянальнасць", "Фіча" } }, + { "given", { "* ", "Няхай ", "Дадзена " } }, + { "rule", { "Правілы" } }, + { "scenario", { "Сцэнарый", "Cцэнар" } }, + { "scenarioOutline", { "Шаблон сцэнарыя", "Узор сцэнара" } }, + { "then", { "* ", "Тады " } }, + { "when", { "* ", "Калі " } } + } + }, + { + "bg", + { + { "and", { "* ", "И " } }, + { "background", { "Предистория" } }, + { "but", { "* ", "Но " } }, + { "examples", { "Примери" } }, + { "feature", { "Функционалност" } }, + { "given", { "* ", "Дадено " } }, + { "rule", { "Правило" } }, + { "scenario", { "Пример", "Сценарий" } }, + { "scenarioOutline", { "Рамка на сценарий" } }, + { "then", { "* ", "То " } }, + { "when", { "* ", "Когато " } } + } + }, + { + "bm", + { + { "and", { "* ", "Dan " } }, + { "background", { "Latar Belakang" } }, + { "but", { "* ", "Tetapi ", "Tapi " } }, + { "examples", { "Contoh" } }, + { "feature", { "Fungsi" } }, + { "given", { "* ", "Diberi ", "Bagi " } }, + { "rule", { "Rule" } }, + { "scenario", { "Senario", "Situasi", "Keadaan" } }, + { "scenarioOutline", { "Kerangka Senario", "Kerangka Situasi", "Kerangka Keadaan", "Garis Panduan Senario" } }, + { "then", { "* ", "Maka ", "Kemudian " } }, + { "when", { "* ", "Apabila " } } + } + }, + { + "bs", + { + { "and", { "* ", "I ", "A " } }, + { "background", { "Pozadina" } }, + { "but", { "* ", "Ali " } }, + { "examples", { "Primjeri" } }, + { "feature", { "Karakteristika" } }, + { "given", { "* ", "Dato " } }, + { "rule", { "Rule" } }, + { "scenario", { "Primjer", "Scenariju", "Scenario" } }, + { "scenarioOutline", { "Scenariju-obris", "Scenario-outline" } }, + { "then", { "* ", "Zatim " } }, + { "when", { "* ", "Kada " } } + } + }, + { + "ca", + { + { "and", { "* ", "I " } }, + { "background", { "Rerefons", "Antecedents" } }, + { "but", { "* ", "Però " } }, + { "examples", { "Exemples" } }, + { "feature", { "Característica", "Funcionalitat" } }, + { "given", { "* ", "Donat ", "Donada ", "Atès ", "Atesa " } }, + { "rule", { "Rule" } }, + { "scenario", { "Exemple", "Escenari" } }, + { "scenarioOutline", { "Esquema de l'escenari" } }, + { "then", { "* ", "Aleshores ", "Cal " } }, + { "when", { "* ", "Quan " } } + } + }, + { + "cs", + { + { "and", { "* ", "A také ", "A " } }, + { "background", { "Pozadí", "Kontext" } }, + { "but", { "* ", "Ale " } }, + { "examples", { "Příklady" } }, + { "feature", { "Požadavek" } }, + { "given", { "* ", "Pokud ", "Za předpokladu " } }, + { "rule", { "Pravidlo" } }, + { "scenario", { "Příklad", "Scénář" } }, + { "scenarioOutline", { "Náčrt Scénáře", "Osnova scénáře" } }, + { "then", { "* ", "Pak " } }, + { "when", { "* ", "Když " } } + } + }, + { + "cy-GB", + { + { "and", { "* ", "A " } }, + { "background", { "Cefndir" } }, + { "but", { "* ", "Ond " } }, + { "examples", { "Enghreifftiau" } }, + { "feature", { "Arwedd" } }, + { "given", { "* ", "Anrhegedig a " } }, + { "rule", { "Rule" } }, + { "scenario", { "Enghraifft", "Scenario" } }, + { "scenarioOutline", { "Scenario Amlinellol" } }, + { "then", { "* ", "Yna " } }, + { "when", { "* ", "Pryd " } } + } + }, + { + "da", + { + { "and", { "* ", "Og " } }, + { "background", { "Baggrund" } }, + { "but", { "* ", "Men " } }, + { "examples", { "Eksempler" } }, + { "feature", { "Egenskab" } }, + { "given", { "* ", "Givet " } }, + { "rule", { "Rule" } }, + { "scenario", { "Eksempel", "Scenarie" } }, + { "scenarioOutline", { "Abstrakt Scenario" } }, + { "then", { "* ", "Så " } }, + { "when", { "* ", "Når " } } + } + }, + { + "de", + { + { "and", { "* ", "Und " } }, + { "background", { "Grundlage", "Hintergrund", "Voraussetzungen", "Vorbedingungen" } }, + { "but", { "* ", "Aber " } }, + { "examples", { "Beispiele" } }, + { "feature", { "Funktionalität", "Funktion" } }, + { "given", { "* ", "Angenommen ", "Gegeben sei ", "Gegeben seien " } }, + { "rule", { "Rule", "Regel" } }, + { "scenario", { "Beispiel", "Szenario" } }, + { "scenarioOutline", { "Szenariogrundriss", "Szenarien" } }, + { "then", { "* ", "Dann " } }, + { "when", { "* ", "Wenn " } } + } + }, + { + "el", + { + { "and", { "* ", "Και " } }, + { "background", { "Υπόβαθρο" } }, + { "but", { "* ", "Αλλά " } }, + { "examples", { "Παραδείγματα", "Σενάρια" } }, + { "feature", { "Δυνατότητα", "Λειτουργία" } }, + { "given", { "* ", "Δεδομένου " } }, + { "rule", { "Rule" } }, + { "scenario", { "Παράδειγμα", "Σενάριο" } }, + { "scenarioOutline", { "Περιγραφή Σεναρίου", "Περίγραμμα Σεναρίου" } }, + { "then", { "* ", "Τότε " } }, + { "when", { "* ", "Όταν " } } + } + }, + { + "em", + { + { "and", { "* ", "😂" } }, + { "background", { "💤" } }, + { "but", { "* ", "😔" } }, + { "examples", { "📓" } }, + { "feature", { "📚" } }, + { "given", { "* ", "😐" } }, + { "rule", { "Rule" } }, + { "scenario", { "🥒", "📕" } }, + { "scenarioOutline", { "📖" } }, + { "then", { "* ", "🙏" } }, + { "when", { "* ", "🎬" } } + } + }, + { + "en", + { + { "and", { "* ", "And " } }, + { "background", { "Background" } }, + { "but", { "* ", "But " } }, + { "examples", { "Examples", "Scenarios" } }, + { "feature", { "Feature", "Business Need", "Ability" } }, + { "given", { "* ", "Given " } }, + { "rule", { "Rule" } }, + { "scenario", { "Example", "Scenario" } }, + { "scenarioOutline", { "Scenario Outline", "Scenario Template" } }, + { "then", { "* ", "Then " } }, + { "when", { "* ", "When " } } + } + }, + { + "en-Scouse", + { + { "and", { "* ", "An " } }, + { "background", { "Dis is what went down" } }, + { "but", { "* ", "Buh " } }, + { "examples", { "Examples" } }, + { "feature", { "Feature" } }, + { "given", { "* ", "Givun ", "Youse know when youse got " } }, + { "rule", { "Rule" } }, + { "scenario", { "The thing of it is" } }, + { "scenarioOutline", { "Wharrimean is" } }, + { "then", { "* ", "Dun ", "Den youse gotta " } }, + { "when", { "* ", "Wun ", "Youse know like when " } } + } + }, + { + "en-au", + { + { "and", { "* ", "Too right " } }, + { "background", { "First off" } }, + { "but", { "* ", "Yeah nah " } }, + { "examples", { "You'll wanna" } }, + { "feature", { "Pretty much" } }, + { "given", { "* ", "Y'know " } }, + { "rule", { "Rule" } }, + { "scenario", { "Awww, look mate" } }, + { "scenarioOutline", { "Reckon it's like" } }, + { "then", { "* ", "But at the end of the day I reckon " } }, + { "when", { "* ", "It's just unbelievable " } } + } + }, + { + "en-lol", + { + { "and", { "* ", "AN " } }, + { "background", { "B4" } }, + { "but", { "* ", "BUT " } }, + { "examples", { "EXAMPLZ" } }, + { "feature", { "OH HAI" } }, + { "given", { "* ", "I CAN HAZ " } }, + { "rule", { "Rule" } }, + { "scenario", { "MISHUN" } }, + { "scenarioOutline", { "MISHUN SRSLY" } }, + { "then", { "* ", "DEN " } }, + { "when", { "* ", "WEN " } } + } + }, + { + "en-old", + { + { "and", { "* ", "Ond ", "7 " } }, + { "background", { "Aer", "Ær" } }, + { "but", { "* ", "Ac " } }, + { "examples", { "Se the", "Se þe", "Se ðe" } }, + { "feature", { "Hwaet", "Hwæt" } }, + { "given", { "* ", "Thurh ", "Þurh ", "Ðurh " } }, + { "rule", { "Rule" } }, + { "scenario", { "Swa" } }, + { "scenarioOutline", { "Swa hwaer swa", "Swa hwær swa" } }, + { "then", { "* ", "Tha ", "Þa ", "Ða ", "Tha the ", "Þa þe ", "Ða ðe " } }, + { "when", { "* ", "Bæþsealf ", "Bæþsealfa ", "Bæþsealfe ", "Ciricæw ", "Ciricæwe ", "Ciricæwa " } } + } + }, + { + "en-pirate", + { + { "and", { "* ", "Aye " } }, + { "background", { "Yo-ho-ho" } }, + { "but", { "* ", "Avast! " } }, + { "examples", { "Dead men tell no tales" } }, + { "feature", { "Ahoy matey!" } }, + { "given", { "* ", "Gangway! " } }, + { "rule", { "Rule" } }, + { "scenario", { "Heave to" } }, + { "scenarioOutline", { "Shiver me timbers" } }, + { "then", { "* ", "Let go and haul " } }, + { "when", { "* ", "Blimey! " } } + } + }, + { + "en-tx", + { + { "and", { "Come hell or high water " } }, + { "background", { "Lemme tell y'all a story" } }, + { "but", { "Well now hold on, I'll you what " } }, + { "examples", { "Now that's a story longer than a cattle drive in July" } }, + { "feature", { "This ain’t my first rodeo", "All gussied up" } }, + { "given", { "Fixin' to ", "All git out " } }, + { "rule", { "Rule " } }, + { "scenario", { "All hat and no cattle" } }, + { "scenarioOutline", { "Serious as a snake bite", "Busy as a hound in flea season" } }, + { "then", { "There’s no tree but bears some fruit " } }, + { "when", { "Quick out of the chute " } } + } + }, + { + "eo", + { + { "and", { "* ", "Kaj " } }, + { "background", { "Fono" } }, + { "but", { "* ", "Sed " } }, + { "examples", { "Ekzemploj" } }, + { "feature", { "Trajto" } }, + { "given", { "* ", "Donitaĵo ", "Komence " } }, + { "rule", { "Rule" } }, + { "scenario", { "Ekzemplo", "Scenaro", "Kazo" } }, + { "scenarioOutline", { "Konturo de la scenaro", "Skizo", "Kazo-skizo" } }, + { "then", { "* ", "Do " } }, + { "when", { "* ", "Se " } } + } + }, + { + "es", + { + { "and", { "* ", "Y ", "E " } }, + { "background", { "Antecedentes" } }, + { "but", { "* ", "Pero " } }, + { "examples", { "Ejemplos" } }, + { "feature", { "Característica", "Necesidad del negocio", "Requisito" } }, + { "given", { "* ", "Dado ", "Dada ", "Dados ", "Dadas " } }, + { "rule", { "Regla", "Regla de negocio" } }, + { "scenario", { "Ejemplo", "Escenario" } }, + { "scenarioOutline", { "Esquema del escenario" } }, + { "then", { "* ", "Entonces " } }, + { "when", { "* ", "Cuando " } } + } + }, + { + "et", + { + { "and", { "* ", "Ja " } }, + { "background", { "Taust" } }, + { "but", { "* ", "Kuid " } }, + { "examples", { "Juhtumid" } }, + { "feature", { "Omadus" } }, + { "given", { "* ", "Eeldades " } }, + { "rule", { "Reegel" } }, + { "scenario", { "Juhtum", "Stsenaarium" } }, + { "scenarioOutline", { "Raamjuhtum", "Raamstsenaarium" } }, + { "then", { "* ", "Siis " } }, + { "when", { "* ", "Kui " } } + } + }, + { + "fa", + { + { "and", { "* ", "و " } }, + { "background", { "زمینه" } }, + { "but", { "* ", "اما " } }, + { "examples", { "نمونه ها" } }, + { "feature", { "وِیژگی" } }, + { "given", { "* ", "با فرض " } }, + { "rule", { "Rule" } }, + { "scenario", { "مثال", "سناریو" } }, + { "scenarioOutline", { "الگوی سناریو" } }, + { "then", { "* ", "آنگاه " } }, + { "when", { "* ", "هنگامی " } } + } + }, + { + "fi", + { + { "and", { "* ", "Ja " } }, + { "background", { "Tausta" } }, + { "but", { "* ", "Mutta " } }, + { "examples", { "Tapaukset" } }, + { "feature", { "Ominaisuus" } }, + { "given", { "* ", "Oletetaan " } }, + { "rule", { "Rule" } }, + { "scenario", { "Tapaus" } }, + { "scenarioOutline", { "Tapausaihio" } }, + { "then", { "* ", "Niin " } }, + { "when", { "* ", "Kun " } } + } + }, + { + "fr", + { + { "and", { "* ", "Et que ", "Et qu'", "Et " } }, + { "background", { "Contexte" } }, + { "but", { "* ", "Mais que ", "Mais qu'", "Mais " } }, + { "examples", { "Exemples" } }, + { "feature", { "Fonctionnalité" } }, + { "given", { "* ", "Soit ", "Sachant que ", "Sachant qu'", "Sachant ", "Etant donné que ", "Etant donné qu'", "Etant donné ", "Etant donnée ", "Etant donnés ", "Etant données ", "Étant donné que ", "Étant donné qu'", "Étant donné ", "Étant donnée ", "Étant donnés ", "Étant données " } }, + { "rule", { "Règle" } }, + { "scenario", { "Exemple", "Scénario" } }, + { "scenarioOutline", { "Plan du scénario", "Plan du Scénario" } }, + { "then", { "* ", "Alors ", "Donc " } }, + { "when", { "* ", "Quand ", "Lorsque ", "Lorsqu'" } } + } + }, + { + "ga", + { + { "and", { "* ", "Agus" } }, + { "background", { "Cúlra" } }, + { "but", { "* ", "Ach" } }, + { "examples", { "Samplaí" } }, + { "feature", { "Gné" } }, + { "given", { "* ", "Cuir i gcás go", "Cuir i gcás nach", "Cuir i gcás gur", "Cuir i gcás nár" } }, + { "rule", { "Rule" } }, + { "scenario", { "Sampla", "Cás" } }, + { "scenarioOutline", { "Cás Achomair" } }, + { "then", { "* ", "Ansin" } }, + { "when", { "* ", "Nuair a", "Nuair nach", "Nuair ba", "Nuair nár" } } + } + }, + { + "gj", + { + { "and", { "* ", "અને " } }, + { "background", { "બેકગ્રાઉન્ડ" } }, + { "but", { "* ", "પણ " } }, + { "examples", { "ઉદાહરણો" } }, + { "feature", { "લક્ષણ", "વ્યાપાર જરૂર", "ક્ષમતા" } }, + { "given", { "* ", "આપેલ છે " } }, + { "rule", { "Rule" } }, + { "scenario", { "ઉદાહરણ", "સ્થિતિ" } }, + { "scenarioOutline", { "પરિદ્દશ્ય રૂપરેખા", "પરિદ્દશ્ય ઢાંચો" } }, + { "then", { "* ", "પછી " } }, + { "when", { "* ", "ક્યારે " } } + } + }, + { + "gl", + { + { "and", { "* ", "E " } }, + { "background", { "Contexto" } }, + { "but", { "* ", "Mais ", "Pero " } }, + { "examples", { "Exemplos" } }, + { "feature", { "Característica" } }, + { "given", { "* ", "Dado ", "Dada ", "Dados ", "Dadas " } }, + { "rule", { "Rule" } }, + { "scenario", { "Exemplo", "Escenario" } }, + { "scenarioOutline", { "Esbozo do escenario" } }, + { "then", { "* ", "Entón ", "Logo " } }, + { "when", { "* ", "Cando " } } + } + }, + { + "he", + { + { "and", { "* ", "וגם " } }, + { "background", { "רקע" } }, + { "but", { "* ", "אבל " } }, + { "examples", { "דוגמאות" } }, + { "feature", { "תכונה" } }, + { "given", { "* ", "בהינתן " } }, + { "rule", { "כלל" } }, + { "scenario", { "דוגמא", "תרחיש" } }, + { "scenarioOutline", { "תבנית תרחיש" } }, + { "then", { "* ", "אז ", "אזי " } }, + { "when", { "* ", "כאשר " } } + } + }, + { + "hi", + { + { "and", { "* ", "और ", "तथा " } }, + { "background", { "पृष्ठभूमि" } }, + { "but", { "* ", "पर ", "परन्तु ", "किन्तु " } }, + { "examples", { "उदाहरण" } }, + { "feature", { "रूप लेख" } }, + { "given", { "* ", "अगर ", "यदि ", "चूंकि " } }, + { "rule", { "नियम" } }, + { "scenario", { "परिदृश्य" } }, + { "scenarioOutline", { "परिदृश्य रूपरेखा" } }, + { "then", { "* ", "तब ", "तदा " } }, + { "when", { "* ", "जब ", "कदा " } } + } + }, + { + "hr", + { + { "and", { "* ", "I " } }, + { "background", { "Pozadina" } }, + { "but", { "* ", "Ali " } }, + { "examples", { "Primjeri", "Scenariji" } }, + { "feature", { "Osobina", "Mogućnost", "Mogucnost" } }, + { "given", { "* ", "Zadan ", "Zadani ", "Zadano ", "Ukoliko " } }, + { "rule", { "Rule" } }, + { "scenario", { "Primjer", "Scenarij" } }, + { "scenarioOutline", { "Skica", "Koncept" } }, + { "then", { "* ", "Onda " } }, + { "when", { "* ", "Kada ", "Kad " } } + } + }, + { + "ht", + { + { "and", { "* ", "Ak ", "Epi ", "E " } }, + { "background", { "Kontèks", "Istorik" } }, + { "but", { "* ", "Men " } }, + { "examples", { "Egzanp" } }, + { "feature", { "Karakteristik", "Mak", "Fonksyonalite" } }, + { "given", { "* ", "Sipoze ", "Sipoze ke ", "Sipoze Ke " } }, + { "rule", { "Rule" } }, + { "scenario", { "Senaryo" } }, + { "scenarioOutline", { "Plan senaryo", "Plan Senaryo", "Senaryo deskripsyon", "Senaryo Deskripsyon", "Dyagram senaryo", "Dyagram Senaryo" } }, + { "then", { "* ", "Lè sa a ", "Le sa a " } }, + { "when", { "* ", "Lè ", "Le " } } + } + }, + { + "hu", + { + { "and", { "* ", "És " } }, + { "background", { "Háttér" } }, + { "but", { "* ", "De " } }, + { "examples", { "Példák" } }, + { "feature", { "Jellemző" } }, + { "given", { "* ", "Amennyiben ", "Adott " } }, + { "rule", { "Szabály" } }, + { "scenario", { "Példa", "Forgatókönyv" } }, + { "scenarioOutline", { "Forgatókönyv vázlat" } }, + { "then", { "* ", "Akkor " } }, + { "when", { "* ", "Majd ", "Ha ", "Amikor " } } + } + }, + { + "id", + { + { "and", { "* ", "Dan " } }, + { "background", { "Dasar", "Latar Belakang" } }, + { "but", { "* ", "Tapi ", "Tetapi " } }, + { "examples", { "Contoh", "Misal" } }, + { "feature", { "Fitur" } }, + { "given", { "* ", "Dengan ", "Diketahui ", "Diasumsikan ", "Bila ", "Jika " } }, + { "rule", { "Rule", "Aturan" } }, + { "scenario", { "Skenario" } }, + { "scenarioOutline", { "Skenario konsep", "Garis-Besar Skenario" } }, + { "then", { "* ", "Maka ", "Kemudian " } }, + { "when", { "* ", "Ketika " } } + } + }, + { + "is", + { + { "and", { "* ", "Og " } }, + { "background", { "Bakgrunnur" } }, + { "but", { "* ", "En " } }, + { "examples", { "Dæmi", "Atburðarásir" } }, + { "feature", { "Eiginleiki" } }, + { "given", { "* ", "Ef " } }, + { "rule", { "Rule" } }, + { "scenario", { "Atburðarás" } }, + { "scenarioOutline", { "Lýsing Atburðarásar", "Lýsing Dæma" } }, + { "then", { "* ", "Þá " } }, + { "when", { "* ", "Þegar " } } + } + }, + { + "it", + { + { "and", { "* ", "E " } }, + { "background", { "Contesto" } }, + { "but", { "* ", "Ma " } }, + { "examples", { "Esempi" } }, + { "feature", { "Funzionalità", "Esigenza di Business", "Abilità" } }, + { "given", { "* ", "Dato ", "Data ", "Dati ", "Date " } }, + { "rule", { "Regola" } }, + { "scenario", { "Esempio", "Scenario" } }, + { "scenarioOutline", { "Schema dello scenario" } }, + { "then", { "* ", "Allora " } }, + { "when", { "* ", "Quando " } } + } + }, + { + "ja", + { + { "and", { "* ", "且つ", "かつ" } }, + { "background", { "背景" } }, + { "but", { "* ", "然し", "しかし", "但し", "ただし" } }, + { "examples", { "例", "サンプル" } }, + { "feature", { "フィーチャ", "機能" } }, + { "given", { "* ", "前提" } }, + { "rule", { "ルール" } }, + { "scenario", { "シナリオ" } }, + { "scenarioOutline", { "シナリオアウトライン", "シナリオテンプレート", "テンプレ", "シナリオテンプレ" } }, + { "then", { "* ", "ならば" } }, + { "when", { "* ", "もし" } } + } + }, + { + "jv", + { + { "and", { "* ", "Lan " } }, + { "background", { "Dasar" } }, + { "but", { "* ", "Tapi ", "Nanging ", "Ananging " } }, + { "examples", { "Conto", "Contone" } }, + { "feature", { "Fitur" } }, + { "given", { "* ", "Nalika ", "Nalikaning " } }, + { "rule", { "Rule" } }, + { "scenario", { "Skenario" } }, + { "scenarioOutline", { "Konsep skenario" } }, + { "then", { "* ", "Njuk ", "Banjur " } }, + { "when", { "* ", "Manawa ", "Menawa " } } + } + }, + { + "ka", + { + { "and", { "* ", "და ", "ასევე " } }, + { "background", { "კონტექსტი" } }, + { "but", { "* ", "მაგრამ ", "თუმცა " } }, + { "examples", { "მაგალითები" } }, + { "feature", { "თვისება", "მოთხოვნა" } }, + { "given", { "* ", "მოცემული ", "მოცემულია ", "ვთქვათ " } }, + { "rule", { "წესი" } }, + { "scenario", { "მაგალითად", "მაგალითი", "მაგ", "სცენარი" } }, + { "scenarioOutline", { "სცენარის ნიმუში", "სცენარის შაბლონი", "ნიმუში", "შაბლონი" } }, + { "then", { "* ", "მაშინ " } }, + { "when", { "* ", "როდესაც ", "როცა ", "როგორც კი ", "თუ " } } + } + }, + { + "kn", + { + { "and", { "* ", "ಮತ್ತು " } }, + { "background", { "ಹಿನ್ನೆಲೆ" } }, + { "but", { "* ", "ಆದರೆ " } }, + { "examples", { "ಉದಾಹರಣೆಗಳು" } }, + { "feature", { "ಹೆಚ್ಚಳ" } }, + { "given", { "* ", "ನೀಡಿದ " } }, + { "rule", { "Rule" } }, + { "scenario", { "ಉದಾಹರಣೆ", "ಕಥಾಸಾರಾಂಶ" } }, + { "scenarioOutline", { "ವಿವರಣೆ" } }, + { "then", { "* ", "ನಂತರ " } }, + { "when", { "* ", "ಸ್ಥಿತಿಯನ್ನು " } } + } + }, + { + "ko", + { + { "and", { "* ", "그리고" } }, + { "background", { "배경" } }, + { "but", { "* ", "하지만", "단" } }, + { "examples", { "예" } }, + { "feature", { "기능" } }, + { "given", { "* ", "조건", "먼저" } }, + { "rule", { "Rule" } }, + { "scenario", { "시나리오" } }, + { "scenarioOutline", { "시나리오 개요" } }, + { "then", { "* ", "그러면" } }, + { "when", { "* ", "만일", "만약" } } + } + }, + { + "lt", + { + { "and", { "* ", "Ir " } }, + { "background", { "Kontekstas" } }, + { "but", { "* ", "Bet " } }, + { "examples", { "Pavyzdžiai", "Scenarijai", "Variantai" } }, + { "feature", { "Savybė" } }, + { "given", { "* ", "Duota " } }, + { "rule", { "Rule" } }, + { "scenario", { "Pavyzdys", "Scenarijus" } }, + { "scenarioOutline", { "Scenarijaus šablonas" } }, + { "then", { "* ", "Tada " } }, + { "when", { "* ", "Kai " } } + } + }, + { + "lu", + { + { "and", { "* ", "an ", "a " } }, + { "background", { "Hannergrond" } }, + { "but", { "* ", "awer ", "mä " } }, + { "examples", { "Beispiller" } }, + { "feature", { "Funktionalitéit" } }, + { "given", { "* ", "ugeholl " } }, + { "rule", { "Rule" } }, + { "scenario", { "Beispill", "Szenario" } }, + { "scenarioOutline", { "Plang vum Szenario" } }, + { "then", { "* ", "dann " } }, + { "when", { "* ", "wann " } } + } + }, + { + "lv", + { + { "and", { "* ", "Un " } }, + { "background", { "Konteksts", "Situācija" } }, + { "but", { "* ", "Bet " } }, + { "examples", { "Piemēri", "Paraugs" } }, + { "feature", { "Funkcionalitāte", "Fīča" } }, + { "given", { "* ", "Kad " } }, + { "rule", { "Rule" } }, + { "scenario", { "Piemērs", "Scenārijs" } }, + { "scenarioOutline", { "Scenārijs pēc parauga" } }, + { "then", { "* ", "Tad " } }, + { "when", { "* ", "Ja " } } + } + }, + { + "mk-Cyrl", + { + { "and", { "* ", "И " } }, + { "background", { "Контекст", "Содржина" } }, + { "but", { "* ", "Но " } }, + { "examples", { "Примери", "Сценарија" } }, + { "feature", { "Функционалност", "Бизнис потреба", "Можност" } }, + { "given", { "* ", "Дадено ", "Дадена " } }, + { "rule", { "Rule" } }, + { "scenario", { "Пример", "Сценарио", "На пример" } }, + { "scenarioOutline", { "Преглед на сценарија", "Скица", "Концепт" } }, + { "then", { "* ", "Тогаш " } }, + { "when", { "* ", "Кога " } } + } + }, + { + "mk-Latn", + { + { "and", { "* ", "I " } }, + { "background", { "Kontekst", "Sodrzhina" } }, + { "but", { "* ", "No " } }, + { "examples", { "Primeri", "Scenaria" } }, + { "feature", { "Funkcionalnost", "Biznis potreba", "Mozhnost" } }, + { "given", { "* ", "Dadeno ", "Dadena " } }, + { "rule", { "Rule" } }, + { "scenario", { "Scenario", "Na primer" } }, + { "scenarioOutline", { "Pregled na scenarija", "Skica", "Koncept" } }, + { "then", { "* ", "Togash " } }, + { "when", { "* ", "Koga " } } + } + }, + { + "mn", + { + { "and", { "* ", "Мөн ", "Тэгээд " } }, + { "background", { "Агуулга" } }, + { "but", { "* ", "Гэхдээ ", "Харин " } }, + { "examples", { "Тухайлбал" } }, + { "feature", { "Функц", "Функционал" } }, + { "given", { "* ", "Өгөгдсөн нь ", "Анх " } }, + { "rule", { "Rule" } }, + { "scenario", { "Сценар" } }, + { "scenarioOutline", { "Сценарын төлөвлөгөө" } }, + { "then", { "* ", "Тэгэхэд ", "Үүний дараа " } }, + { "when", { "* ", "Хэрэв " } } + } + }, + { + "ne", + { + { "and", { "* ", "र ", "अनि " } }, + { "background", { "पृष्ठभूमी" } }, + { "but", { "* ", "तर " } }, + { "examples", { "उदाहरण", "उदाहरणहरु" } }, + { "feature", { "सुविधा", "विशेषता" } }, + { "given", { "* ", "दिइएको ", "दिएको ", "यदि " } }, + { "rule", { "नियम" } }, + { "scenario", { "परिदृश्य" } }, + { "scenarioOutline", { "परिदृश्य रूपरेखा" } }, + { "then", { "* ", "त्यसपछि ", "अनी " } }, + { "when", { "* ", "जब " } } + } + }, + { + "nl", + { + { "and", { "* ", "En " } }, + { "background", { "Achtergrond" } }, + { "but", { "* ", "Maar " } }, + { "examples", { "Voorbeelden" } }, + { "feature", { "Functionaliteit" } }, + { "given", { "* ", "Gegeven ", "Stel " } }, + { "rule", { "Rule" } }, + { "scenario", { "Voorbeeld", "Scenario" } }, + { "scenarioOutline", { "Abstract Scenario" } }, + { "then", { "* ", "Dan " } }, + { "when", { "* ", "Als ", "Wanneer " } } + } + }, + { + "no", + { + { "and", { "* ", "Og " } }, + { "background", { "Bakgrunn" } }, + { "but", { "* ", "Men " } }, + { "examples", { "Eksempler" } }, + { "feature", { "Egenskap" } }, + { "given", { "* ", "Gitt " } }, + { "rule", { "Regel" } }, + { "scenario", { "Eksempel", "Scenario" } }, + { "scenarioOutline", { "Scenariomal", "Abstrakt Scenario" } }, + { "then", { "* ", "Så " } }, + { "when", { "* ", "Når " } } + } + }, + { + "pa", + { + { "and", { "* ", "ਅਤੇ " } }, + { "background", { "ਪਿਛੋਕੜ" } }, + { "but", { "* ", "ਪਰ " } }, + { "examples", { "ਉਦਾਹਰਨਾਂ" } }, + { "feature", { "ਖਾਸੀਅਤ", "ਮੁਹਾਂਦਰਾ", "ਨਕਸ਼ ਨੁਹਾਰ" } }, + { "given", { "* ", "ਜੇਕਰ ", "ਜਿਵੇਂ ਕਿ " } }, + { "rule", { "Rule" } }, + { "scenario", { "ਉਦਾਹਰਨ", "ਪਟਕਥਾ" } }, + { "scenarioOutline", { "ਪਟਕਥਾ ਢਾਂਚਾ", "ਪਟਕਥਾ ਰੂਪ ਰੇਖਾ" } }, + { "then", { "* ", "ਤਦ " } }, + { "when", { "* ", "ਜਦੋਂ " } } + } + }, + { + "pl", + { + { "and", { "* ", "Oraz ", "I " } }, + { "background", { "Założenia" } }, + { "but", { "* ", "Ale " } }, + { "examples", { "Przykłady" } }, + { "feature", { "Właściwość", "Funkcja", "Aspekt", "Potrzeba biznesowa" } }, + { "given", { "* ", "Zakładając ", "Mając ", "Zakładając, że " } }, + { "rule", { "Zasada", "Reguła" } }, + { "scenario", { "Przykład", "Scenariusz" } }, + { "scenarioOutline", { "Szablon scenariusza" } }, + { "then", { "* ", "Wtedy " } }, + { "when", { "* ", "Jeżeli ", "Jeśli ", "Gdy ", "Kiedy " } } + } + }, + { + "pt", + { + { "and", { "* ", "E " } }, + { "background", { "Contexto", "Cenário de Fundo", "Cenario de Fundo", "Fundo" } }, + { "but", { "* ", "Mas " } }, + { "examples", { "Exemplos", "Cenários", "Cenarios" } }, + { "feature", { "Funcionalidade", "Característica", "Caracteristica" } }, + { "given", { "* ", "Dado ", "Dada ", "Dados ", "Dadas " } }, + { "rule", { "Regra" } }, + { "scenario", { "Exemplo", "Cenário", "Cenario" } }, + { "scenarioOutline", { "Esquema do Cenário", "Esquema do Cenario", "Delineação do Cenário", "Delineacao do Cenario" } }, + { "then", { "* ", "Então ", "Entao " } }, + { "when", { "* ", "Quando " } } + } + }, + { + "ro", + { + { "and", { "* ", "Si ", "Și ", "Şi " } }, + { "background", { "Context" } }, + { "but", { "* ", "Dar " } }, + { "examples", { "Exemple" } }, + { "feature", { "Functionalitate", "Funcționalitate", "Funcţionalitate" } }, + { "given", { "* ", "Date fiind ", "Dat fiind ", "Dată fiind", "Dati fiind ", "Dați fiind ", "Daţi fiind " } }, + { "rule", { "Rule" } }, + { "scenario", { "Exemplu", "Scenariu" } }, + { "scenarioOutline", { "Structura scenariu", "Structură scenariu" } }, + { "then", { "* ", "Atunci " } }, + { "when", { "* ", "Cand ", "Când " } } + } + }, + { + "ru", + { + { "and", { "* ", "И ", "К тому же ", "Также " } }, + { "background", { "Предыстория", "Контекст" } }, + { "but", { "* ", "Но ", "А ", "Иначе " } }, + { "examples", { "Примеры" } }, + { "feature", { "Функция", "Функциональность", "Функционал", "Свойство", "Фича" } }, + { "given", { "* ", "Допустим ", "Дано ", "Пусть " } }, + { "rule", { "Правило" } }, + { "scenario", { "Пример", "Сценарий" } }, + { "scenarioOutline", { "Структура сценария", "Шаблон сценария" } }, + { "then", { "* ", "То ", "Затем ", "Тогда " } }, + { "when", { "* ", "Когда ", "Если " } } + } + }, + { + "sk", + { + { "and", { "* ", "A ", "A tiež ", "A taktiež ", "A zároveň " } }, + { "background", { "Pozadie" } }, + { "but", { "* ", "Ale " } }, + { "examples", { "Príklady" } }, + { "feature", { "Požiadavka", "Funkcia", "Vlastnosť" } }, + { "given", { "* ", "Pokiaľ ", "Za predpokladu " } }, + { "rule", { "Rule" } }, + { "scenario", { "Príklad", "Scenár" } }, + { "scenarioOutline", { "Náčrt Scenáru", "Náčrt Scenára", "Osnova Scenára" } }, + { "then", { "* ", "Tak ", "Potom " } }, + { "when", { "* ", "Keď ", "Ak " } } + } + }, + { + "sl", + { + { "and", { "In ", "Ter " } }, + { "background", { "Kontekst", "Osnova", "Ozadje" } }, + { "but", { "Toda ", "Ampak ", "Vendar " } }, + { "examples", { "Primeri", "Scenariji" } }, + { "feature", { "Funkcionalnost", "Funkcija", "Možnosti", "Moznosti", "Lastnost", "Značilnost" } }, + { "given", { "Dano ", "Podano ", "Zaradi ", "Privzeto " } }, + { "rule", { "Rule" } }, + { "scenario", { "Primer", "Scenarij" } }, + { "scenarioOutline", { "Struktura scenarija", "Skica", "Koncept", "Oris scenarija", "Osnutek" } }, + { "then", { "Nato ", "Potem ", "Takrat " } }, + { "when", { "Ko ", "Ce ", "Če ", "Kadar " } } + } + }, + { + "sr-Cyrl", + { + { "and", { "* ", "И " } }, + { "background", { "Контекст", "Основа", "Позадина" } }, + { "but", { "* ", "Али " } }, + { "examples", { "Примери", "Сценарији" } }, + { "feature", { "Функционалност", "Могућност", "Особина" } }, + { "given", { "* ", "За дато ", "За дате ", "За дати " } }, + { "rule", { "Правило" } }, + { "scenario", { "Пример", "Сценарио", "Пример" } }, + { "scenarioOutline", { "Структура сценарија", "Скица", "Концепт" } }, + { "then", { "* ", "Онда " } }, + { "when", { "* ", "Када ", "Кад " } } + } + }, + { + "sr-Latn", + { + { "and", { "* ", "I " } }, + { "background", { "Kontekst", "Osnova", "Pozadina" } }, + { "but", { "* ", "Ali " } }, + { "examples", { "Primeri", "Scenariji" } }, + { "feature", { "Funkcionalnost", "Mogućnost", "Mogucnost", "Osobina" } }, + { "given", { "* ", "Za dato ", "Za date ", "Za dati " } }, + { "rule", { "Pravilo" } }, + { "scenario", { "Scenario", "Primer" } }, + { "scenarioOutline", { "Struktura scenarija", "Skica", "Koncept" } }, + { "then", { "* ", "Onda " } }, + { "when", { "* ", "Kada ", "Kad " } } + } + }, + { + "sv", + { + { "and", { "* ", "Och " } }, + { "background", { "Bakgrund" } }, + { "but", { "* ", "Men " } }, + { "examples", { "Exempel" } }, + { "feature", { "Egenskap" } }, + { "given", { "* ", "Givet " } }, + { "rule", { "Regel" } }, + { "scenario", { "Scenario" } }, + { "scenarioOutline", { "Abstrakt Scenario", "Scenariomall" } }, + { "then", { "* ", "Så " } }, + { "when", { "* ", "När " } } + } + }, + { + "ta", + { + { "and", { "* ", "மேலும் ", "மற்றும் " } }, + { "background", { "பின்னணி" } }, + { "but", { "* ", "ஆனால் " } }, + { "examples", { "எடுத்துக்காட்டுகள்", "காட்சிகள்", "நிலைமைகளில்" } }, + { "feature", { "அம்சம்", "வணிக தேவை", "திறன்" } }, + { "given", { "* ", "கொடுக்கப்பட்ட " } }, + { "rule", { "Rule" } }, + { "scenario", { "உதாரணமாக", "காட்சி" } }, + { "scenarioOutline", { "காட்சி சுருக்கம்", "காட்சி வார்ப்புரு" } }, + { "then", { "* ", "அப்பொழுது " } }, + { "when", { "* ", "எப்போது " } } + } + }, + { + "th", + { + { "and", { "* ", "และ " } }, + { "background", { "แนวคิด" } }, + { "but", { "* ", "แต่ " } }, + { "examples", { "ชุดของตัวอย่าง", "ชุดของเหตุการณ์" } }, + { "feature", { "โครงหลัก", "ความต้องการทางธุรกิจ", "ความสามารถ" } }, + { "given", { "* ", "กำหนดให้ " } }, + { "rule", { "Rule" } }, + { "scenario", { "เหตุการณ์" } }, + { "scenarioOutline", { "สรุปเหตุการณ์", "โครงสร้างของเหตุการณ์" } }, + { "then", { "* ", "ดังนั้น " } }, + { "when", { "* ", "เมื่อ " } } + } + }, + { + "te", + { + { "and", { "* ", "మరియు " } }, + { "background", { "నేపథ్యం" } }, + { "but", { "* ", "కాని " } }, + { "examples", { "ఉదాహరణలు" } }, + { "feature", { "గుణము" } }, + { "given", { "* ", "చెప్పబడినది " } }, + { "rule", { "Rule" } }, + { "scenario", { "ఉదాహరణ", "సన్నివేశం" } }, + { "scenarioOutline", { "కథనం" } }, + { "then", { "* ", "అప్పుడు " } }, + { "when", { "* ", "ఈ పరిస్థితిలో " } } + } + }, + { + "tlh", + { + { "and", { "* ", "'ej ", "latlh " } }, + { "background", { "mo'" } }, + { "but", { "* ", "'ach ", "'a " } }, + { "examples", { "ghantoH", "lutmey" } }, + { "feature", { "Qap", "Qu'meH 'ut", "perbogh", "poQbogh malja'", "laH" } }, + { "given", { "* ", "ghu' noblu' ", "DaH ghu' bejlu' " } }, + { "rule", { "Rule" } }, + { "scenario", { "lut" } }, + { "scenarioOutline", { "lut chovnatlh" } }, + { "then", { "* ", "vaj " } }, + { "when", { "* ", "qaSDI' " } } + } + }, + { + "tr", + { + { "and", { "* ", "Ve " } }, + { "background", { "Geçmiş" } }, + { "but", { "* ", "Fakat ", "Ama " } }, + { "examples", { "Örnekler" } }, + { "feature", { "Özellik" } }, + { "given", { "* ", "Diyelim ki " } }, + { "rule", { "Kural" } }, + { "scenario", { "Örnek", "Senaryo" } }, + { "scenarioOutline", { "Senaryo taslağı" } }, + { "then", { "* ", "O zaman " } }, + { "when", { "* ", "Eğer ki " } } + } + }, + { + "tt", + { + { "and", { "* ", "Һәм ", "Вә " } }, + { "background", { "Кереш" } }, + { "but", { "* ", "Ләкин ", "Әмма " } }, + { "examples", { "Үрнәкләр", "Мисаллар" } }, + { "feature", { "Мөмкинлек", "Үзенчәлеклелек" } }, + { "given", { "* ", "Әйтик " } }, + { "rule", { "Rule" } }, + { "scenario", { "Сценарий" } }, + { "scenarioOutline", { "Сценарийның төзелеше" } }, + { "then", { "* ", "Нәтиҗәдә " } }, + { "when", { "* ", "Әгәр " } } + } + }, + { + "uk", + { + { "and", { "* ", "І ", "А також ", "Та " } }, + { "background", { "Передумова" } }, + { "but", { "* ", "Але " } }, + { "examples", { "Приклади" } }, + { "feature", { "Функціонал" } }, + { "given", { "* ", "Припустимо ", "Припустимо, що ", "Нехай ", "Дано " } }, + { "rule", { "Rule" } }, + { "scenario", { "Приклад", "Сценарій" } }, + { "scenarioOutline", { "Структура сценарію" } }, + { "then", { "* ", "То ", "Тоді " } }, + { "when", { "* ", "Якщо ", "Коли " } } + } + }, + { + "ur", + { + { "and", { "* ", "اور " } }, + { "background", { "پس منظر" } }, + { "but", { "* ", "لیکن " } }, + { "examples", { "مثالیں" } }, + { "feature", { "صلاحیت", "کاروبار کی ضرورت", "خصوصیت" } }, + { "given", { "* ", "اگر ", "بالفرض ", "فرض کیا " } }, + { "rule", { "Rule" } }, + { "scenario", { "منظرنامہ" } }, + { "scenarioOutline", { "منظر نامے کا خاکہ" } }, + { "then", { "* ", "پھر ", "تب " } }, + { "when", { "* ", "جب " } } + } + }, + { + "uz", + { + { "and", { "* ", "Ва " } }, + { "background", { "Тарих" } }, + { "but", { "* ", "Лекин ", "Бирок ", "Аммо " } }, + { "examples", { "Мисоллар" } }, + { "feature", { "Функционал" } }, + { "given", { "* ", "Belgilangan " } }, + { "rule", { "Rule" } }, + { "scenario", { "Сценарий" } }, + { "scenarioOutline", { "Сценарий структураси" } }, + { "then", { "* ", "Унда " } }, + { "when", { "* ", "Агар " } } + } + }, + { + "vi", + { + { "and", { "* ", "Và " } }, + { "background", { "Bối cảnh" } }, + { "but", { "* ", "Nhưng " } }, + { "examples", { "Dữ liệu" } }, + { "feature", { "Tính năng" } }, + { "given", { "* ", "Biết ", "Cho " } }, + { "rule", { "Rule" } }, + { "scenario", { "Tình huống", "Kịch bản" } }, + { "scenarioOutline", { "Khung tình huống", "Khung kịch bản" } }, + { "then", { "* ", "Thì " } }, + { "when", { "* ", "Khi " } } + } + }, + { + "zh-CN", + { + { "and", { "* ", "而且", "并且", "同时" } }, + { "background", { "背景" } }, + { "but", { "* ", "但是" } }, + { "examples", { "例子" } }, + { "feature", { "功能" } }, + { "given", { "* ", "假如", "假设", "假定" } }, + { "rule", { "Rule", "规则" } }, + { "scenario", { "场景", "剧本" } }, + { "scenarioOutline", { "场景大纲", "剧本大纲" } }, + { "then", { "* ", "那么" } }, + { "when", { "* ", "当" } } + } + }, + { + "zh-TW", + { + { "and", { "* ", "而且", "並且", "同時" } }, + { "background", { "背景" } }, + { "but", { "* ", "但是" } }, + { "examples", { "例子" } }, + { "feature", { "功能" } }, + { "given", { "* ", "假如", "假設", "假定" } }, + { "rule", { "Rule" } }, + { "scenario", { "場景", "劇本" } }, + { "scenarioOutline", { "場景大綱", "劇本大綱" } }, + { "then", { "* ", "那麼" } }, + { "when", { "* ", "當" } } + } + }, + { + "mr", + { + { "and", { "* ", "आणि ", "तसेच " } }, + { "background", { "पार्श्वभूमी" } }, + { "but", { "* ", "पण ", "परंतु " } }, + { "examples", { "उदाहरण" } }, + { "feature", { "वैशिष्ट्य", "सुविधा" } }, + { "given", { "* ", "जर", "दिलेल्या प्रमाणे " } }, + { "rule", { "नियम" } }, + { "scenario", { "परिदृश्य" } }, + { "scenarioOutline", { "परिदृश्य रूपरेखा" } }, + { "then", { "* ", "मग ", "तेव्हा " } }, + { "when", { "* ", "जेव्हा " } } + } + }, + { + "amh", + { + { "and", { "* ", "እና " } }, + { "background", { "ቅድመ ሁኔታ", "መነሻ", "መነሻ ሀሳብ" } }, + { "but", { "* ", "ግን " } }, + { "examples", { "ምሳሌዎች", "ሁናቴዎች" } }, + { "feature", { "ስራ", "የተፈለገው ስራ", "የሚፈለገው ድርጊት" } }, + { "given", { "* ", "የተሰጠ " } }, + { "rule", { "ህግ" } }, + { "scenario", { "ምሳሌ", "ሁናቴ" } }, + { "scenarioOutline", { "ሁናቴ ዝርዝር", "ሁናቴ አብነት" } }, + { "then", { "* ", "ከዚያ " } }, + { "when", { "* ", "መቼ " } } + } + } + }; + + return kwms;} + +const keywords_map& +keywords(const std::string_view& language) +{ + return all_keywords().at(language); +} + +} + diff --git a/cpp/src/lib/gherkin/exceptions.cpp b/cpp/src/lib/gherkin/exceptions.cpp new file mode 100644 index 000000000..1dced3398 --- /dev/null +++ b/cpp/src/lib/gherkin/exceptions.cpp @@ -0,0 +1,183 @@ +#include + +#include +#include +#include + +namespace gherkin { + +/////////////////////////////////////////////////////////////////////////////// +// +// parser error +// +/////////////////////////////////////////////////////////////////////////////// +parser_error::parser_error( + const std::string& message, + const cms::location& location +) +: std::runtime_error(make_message(message, location)), +location_(location) +{} + +parser_error::parser_error(const parser_error& other) +: std::runtime_error(other.what()), +location_(other.location_) +{} + +parser_error::~parser_error() +{} + +std::string +parser_error::make_message( + const std::string& message, + const cms::location& location +) const +{ + std::ostringstream oss; + + oss + << "(" << location.line << ":" << location.column.value_or(0) << ")" + << ": " << message; + + return oss.str(); +} + +bool +parser_error::same_message(const parser_error& other) const +{ return std::strcmp(what(), other.what()) == 0; } + +const cms::location& +parser_error::location() const +{ return location_; } + +/////////////////////////////////////////////////////////////////////////////// +// +// no such language error +// +/////////////////////////////////////////////////////////////////////////////// +no_such_language_error::no_such_language_error( + const std::string& language, + const cms::location& location +) +: parser_error("Language not supported: " + language, location) +{} + +no_such_language_error::~no_such_language_error() +{} + +/////////////////////////////////////////////////////////////////////////////// +// +// unexpected token +// +/////////////////////////////////////////////////////////////////////////////// +unexpected_token::unexpected_token( + const token& received_token, + const std::string& expected_tokens, + const std::string& state_comment +) +: parser_error( + make_message(received_token, expected_tokens), + make_location(received_token) +), +received_token_(received_token), +expected_tokens_(expected_tokens), +state_comment_(state_comment) +{} + +unexpected_token::~unexpected_token() +{} + +std::string +unexpected_token::make_message( + const token& received_token, + const std::string& expected_tokens +) const +{ + std::ostringstream oss; + + oss + << "expected: " << expected_tokens + << ", got '" << strip(received_token.value()) << "'" + ; + + return oss.str(); +} + +cms::location +unexpected_token::make_location(const token& t) const +{ + return + t.location.column.value_or(0) > 1 + ? t.location + : cms::location{ + .line = t.location.line, + .column = t.line.indent() + 1 + } + ; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// unexpected eof +// +/////////////////////////////////////////////////////////////////////////////// +unexpected_eof::unexpected_eof( + const token& received_token, + const std::string& expected_tokens, + const std::string& state_comment +) +: parser_error( + make_message(expected_tokens), + received_token.location +), +expected_tokens_(expected_tokens), +state_comment_(state_comment) +{} + +unexpected_eof::~unexpected_eof() +{} + +std::string +unexpected_eof::make_message(const std::string& expected_tokens) const +{ + std::ostringstream oss; + + oss << "unexpected end of file, expected: " << expected_tokens; + + return oss.str(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// composite parser error +// +/////////////////////////////////////////////////////////////////////////////// +composite_parser_error::composite_parser_error(const parser_error_ptrs& ptrs) +: parser_error("", {}), +ptrs_(ptrs) +{} + +composite_parser_error::~composite_parser_error() +{} + +std::string +composite_parser_error::make_message(const parser_error_ptrs& ptrs) const +{ + strings errs; + + for (const auto& p : ptrs) { + errs.push_back(p->what()); + } + + std::ostringstream oss; + + oss << "Parser errors:\n" << join("\n", errs); + + return oss.str(); +} + +const parser_error_ptrs& +composite_parser_error::errors() const +{ return ptrs_; } + +} diff --git a/cpp/src/lib/gherkin/id_generator.cpp b/cpp/src/lib/gherkin/id_generator.cpp new file mode 100644 index 000000000..5cdebc069 --- /dev/null +++ b/cpp/src/lib/gherkin/id_generator.cpp @@ -0,0 +1,21 @@ +#include + +namespace gherkin { + +id_generator_base::id_generator_base() +{} + +id_generator_base::~id_generator_base() +{} + +id_generator::id_generator() +{} + +id_generator::~id_generator() +{} + +std::string +id_generator::next_id() +{ return std::to_string(id_counter_++); } + +} diff --git a/cpp/src/lib/gherkin/keywords.cpp b/cpp/src/lib/gherkin/keywords.cpp new file mode 100644 index 000000000..ca5a276ba --- /dev/null +++ b/cpp/src/lib/gherkin/keywords.cpp @@ -0,0 +1,41 @@ +#include +#include + +namespace gherkin { + +const string_views& +keywords(const std::string_view& language, const std::string_view& kw) +{ return keywords(language).at(kw); } + +string_views +keywords(const std::string_view& language, const string_views& kws) +{ + string_views svs; + + for (const auto& kw : kws) { + auto ksvs = keywords(language, kw); + svs.insert(svs.end(), ksvs.begin(), ksvs.end()); + } + + return svs; +} + +dialect +get_dialect(const std::string_view& language) +{ + return { + .feature_keywords = keywords(language, "feature"), + .rule_keywords = keywords(language, "rule"), + .scenario_keywords = keywords(language, "scenario"), + .scenario_outline_keywords = keywords(language, "scenarioOutline"), + .background_keywords = keywords(language, "background"), + .examples_keywords = keywords(language, "examples"), + .given_keywords = keywords(language, "given"), + .when_keywords = keywords(language, "when"), + .then_keywords = keywords(language, "then"), + .and_keywords = keywords(language, "and"), + .but_keywords = keywords(language, "but") + }; +} + +} diff --git a/cpp/src/lib/gherkin/line.cpp b/cpp/src/lib/gherkin/line.cpp new file mode 100644 index 000000000..c3657373d --- /dev/null +++ b/cpp/src/lib/gherkin/line.cpp @@ -0,0 +1,194 @@ +#include +#include + +#include +#include +#include +#include + +namespace gherkin { + +using unescape_pair = std::pair; +using unescapes = std::vector; + +static const unescapes line_unescapes = { + { "\\\\", "\\" }, + { "\\|", "|" }, + { "\\n", "\n" } +}; + +template +void +split_table_cells(std::string_view row, Callabble&& cell_cb) +{ + auto wrow = to_wide(std::string(row)); + + std::size_t col = 0; + std::size_t start_col = col + 1; + std::wstring cell; + bool first_cell = true; + auto it = wrow.begin(); + auto end = wrow.end(); + auto next_ch = [](auto& it, const auto& end) { + return it != end ? *it++ : 0; + }; + + while (col < row.size()) { + auto ch = next_ch(it, end); + ++col; + + if (ch == '|') { + if (first_cell) { + first_cell = false; + } else { + cell_cb(cell, start_col); + } + + cell.clear(); + start_col = col + 1; + } else if (ch == '\\') { + ch = next_ch(it, end); + ++col; + + if (ch == 'n') { + cell += '\n'; + } else { + if (ch != '|' && ch != '\\') { + cell += '\\'; + } + + cell += ch; + } + } else if (ch) { + cell += ch; + } + } +} + +line::line() +{} + +line::line(const std::string& line_text, std::size_t line_number) +: line_text_(line_text), +line_number_(line_number), +trimmed_line_text_(lstrip(line_text_)) +{ + indent_ = line_text_.size() - trimmed_line_text_.size(); +} + +std::string +line::get_rest_trimmed(std::size_t length) const +{ + auto pos = std::min(length, trimmed_line_text_.size()); + + return strip(trimmed_line_text_.substr(pos)); +} + +std::string +line::get_keyword_trimmed(std::string_view kw) const +{ + // Keyword ends with ':' + return get_rest_trimmed(kw.size() + 1); +} + +std::string_view +line::get_line_text(std::size_t indent_to_remove) const +{ + std::string_view sv; + + if (indent_to_remove == std::string::npos || indent_to_remove > indent_) { + return trimmed_line_text_; + } else { + sv = line_text_; + return sv.substr(indent_to_remove); + } +} + +std::string_view +line::line_text() const +{ return line_text_; } + +std::size_t +line::indent() const +{ return indent_; } + +bool +line::is_empty() const +{ return trimmed_line_text_.empty(); } + +bool +line::startswith(std::string_view prefix) const +{ return trimmed_line_text_.find(prefix) == 0; } + +bool +line::startswith_title_keyword(const std::string& keyword) const +{ return trimmed_line_text_.find(keyword + ":") == 0; } + +items +line::table_cells() const +{ + items items; + + split_table_cells( + trimmed_line_text_, + [&](const auto& cell, auto col) { + using namespace std::literals; + + auto stripped_cell = lstrip(cell, re_pattern::spaces_no_nl); + auto cell_indent = cell.size() - stripped_cell.size(); + stripped_cell = rstrip(stripped_cell, re_pattern::spaces_no_nl); + + item i{ + .column = col + indent_ + cell_indent, + .text = to_narrow(stripped_cell) + }; + + for (const auto& p : line_unescapes) { + subst(i.text, p.first, p.second); + } + + items.emplace_back(std::move(i)); + } + ); + + return items; +} + +items +line::tags() const +{ + items tags; + + auto column = indent_ + 1; + auto items_line = subst(trimmed_line_text_, "\\s+(?:#.*)?$", ""); + auto items = split("@", items_line); + std::regex no_spaces("^\\S+$"); + + for (std::size_t i = 1; i < items.size(); ++i) { + auto original_item = items[i]; + auto sitem = strip(items[i]); + + if (sitem.empty()) { + continue; + } + + if (!full_match(sitem, no_spaces)) { + throw + parser_error( + "A tag may not contain whitespace", + { .line = line_number_, .column = column } + ); + } + + tags.emplace_back(item{ + .column = column, + .text = "@" + sitem + }); + + column += original_item.size() + 1; + } + + return tags; +} + +} diff --git a/cpp/src/lib/gherkin/parse_error.cpp b/cpp/src/lib/gherkin/parse_error.cpp new file mode 100644 index 000000000..d194f4a1d --- /dev/null +++ b/cpp/src/lib/gherkin/parse_error.cpp @@ -0,0 +1,17 @@ +#include + +namespace gherkin { + +json +parse_error::to_json() const +{ + json j; + + j["parseError"]["source"]["uri"] = uri; + location.to_json(j["parseError"]["source"]["location"]); + j["parseError"]["message"] = message; + + return j; +} + +} diff --git a/cpp/src/lib/gherkin/pickle_compiler.cpp b/cpp/src/lib/gherkin/pickle_compiler.cpp new file mode 100644 index 000000000..29e1a3a70 --- /dev/null +++ b/cpp/src/lib/gherkin/pickle_compiler.cpp @@ -0,0 +1,418 @@ +#include +#include +#include + +namespace gherkin { + +cms::pickle_step_type +to_pickle_step_type(cms::step_keyword_type keyword_type) +{ + using step_map_type = std::unordered_map< + cms::step_keyword_type, + cms::pickle_step_type + >; + + static const step_map_type smap = { + { cms::step_keyword_type::UNKNOWN, cms::pickle_step_type::UNKNOWN }, + { cms::step_keyword_type::CONTEXT, cms::pickle_step_type::CONTEXT }, + { cms::step_keyword_type::ACTION, cms::pickle_step_type::ACTION }, + { cms::step_keyword_type::OUTCOME, cms::pickle_step_type::OUTCOME } + }; + + return smap.at(keyword_type); +} + +template +void +append(Vector& to, const Vector& from) +{ to.insert(to.end(), from.begin(), from.end()); } + +pickle_compiler::pickle_compiler() +: pickle_compiler(new_id_generator()) +{} + +pickle_compiler::pickle_compiler(id_generator_ptr idp) +: idp_(idp) +{} + +pickle_compiler::~pickle_compiler() +{} + +pickles +pickle_compiler::compile( + const cms::gherkin_document& d, + const std::string& uri, + pickle_cb sink +) +{ + pickle_compiler_context ctx{ .idp = idp_, .sink = sink }; + + if (d.feature) { + compile_feature(ctx, *d.feature, d.feature->language, uri); + } + + return ctx.pickles; +} + +void +pickle_compiler::compile_feature( + pickle_compiler_context& ctx, + const cms::feature& f, + const std::string& language, + const std::string& uri +) +{ + auto tags = f.tags; + steps background_steps; + + for (const auto& child : f.children) { + if (child.background) { + append(background_steps, child.background->steps); + } else if (child.rule) { + compile_rule( + ctx, + *child.rule, + tags, + background_steps, + language, + uri + ); + } else if (child.scenario) { + const auto& scenario = *child.scenario; + + if (scenario.examples.empty()) { + compile_scenario( + ctx, + scenario, + tags, + background_steps, + language, + uri + ); + } else { + compile_scenario_outline( + ctx, + scenario, + tags, + background_steps, + language, + uri + ); + } + } + } +} + +void +pickle_compiler::compile_rule( + pickle_compiler_context& ctx, + const cms::rule& r, + const tags& parent_tags, + const steps& background_steps, + const std::string& language, + const std::string& uri +) +{ + auto steps = background_steps; + auto tags = parent_tags; + + append(tags, r.tags); + + for (const auto& child : r.children) { + if (child.background) { + append(steps, child.background->steps); + } else if (child.scenario) { + const auto& scenario = *child.scenario; + + if (scenario.examples.empty()) { + compile_scenario( + ctx, + scenario, + tags, + steps, + language, + uri + ); + } else { + compile_scenario_outline( + ctx, + scenario, + tags, + steps, + language, + uri + ); + } + } + } +} + +void +pickle_compiler::compile_scenario( + pickle_compiler_context& ctx, + const cms::scenario& s, + const tags& parent_tags, + const steps& background_steps, + const std::string& language, + const std::string& uri +) +{ + auto conj = cms::step_keyword_type::CONJUNCTION; + pickle_steps steps; + + if (!s.steps.empty()) { + auto ssteps = background_steps; + + append(ssteps, s.steps); + + auto last_keyword_type = cms::step_keyword_type::UNKNOWN; + + for (const auto& step : ssteps) { + if (step.keyword_type && *step.keyword_type != conj) { + last_keyword_type = *step.keyword_type; + } + + steps.push_back(make_pickle_step(step, last_keyword_type)); + } + } + + auto tags = parent_tags; + + append(tags, s.tags); + + strings source_ids = { s.id }; + + cms::pickle p{ + .id = ctx.next_id(), + .uri = uri, + .name = s.name, + .language = language, + .steps = steps, + .tags = make_pickle_tags(tags), + .ast_node_ids = source_ids + }; + + ctx.add_pickle(p); +} + +void +pickle_compiler::compile_scenario_outline( + pickle_compiler_context& ctx, + const cms::scenario& s, + const tags& parent_tags, + const steps& background_steps, + const std::string& language, + const std::string& uri +) +{ + auto conj = cms::step_keyword_type::CONJUNCTION; + + for (const auto& es : s.examples) { + if (!es.table_header) { + continue; + } + + const auto& variable_cells = es.table_header->cells; + + for (const auto& values_row : es.table_body) { + const auto& value_cells = values_row.cells; + + pickle_steps steps; + auto last_keyword_type = cms::step_keyword_type::UNKNOWN; + + if (!s.steps.empty()) { + for (const auto& step : background_steps) { + if (step.keyword_type && *step.keyword_type != conj) { + last_keyword_type = *step.keyword_type; + } + + steps.push_back(make_pickle_step(step, last_keyword_type)); + } + } + + auto tags = parent_tags; + + append(tags, s.tags); + append(tags, es.tags); + + for (const auto& step : s.steps) { + if (step.keyword_type && *step.keyword_type != conj) { + last_keyword_type = *step.keyword_type; + } + + steps.push_back( + make_pickle_step( + step, + variable_cells, + std::addressof(values_row), + last_keyword_type + ) + ); + } + + strings source_ids = { s.id, values_row.id }; + + cms::pickle p{ + .id = ctx.next_id(), + .uri = uri, + .name = interpolate(s.name, variable_cells, value_cells), + .language = language, + .steps = steps, + .tags = make_pickle_tags(tags), + .ast_node_ids = source_ids + }; + + ctx.add_pickle(p); + } + } +} + +cms::pickle_step +pickle_compiler::make_pickle_step( + const cms::step& step, + const table_cells& variable_cells, + const cms::table_row* value_row_ptr, + cms::step_keyword_type keyword_type +) +{ + const auto& value_cells = + value_row_ptr + ? value_row_ptr->cells + : table_cells() + ; + + cms::pickle_step ps{ + .ast_node_ids = { step.id }, + .id = next_id(), + .type = to_pickle_step_type(keyword_type), + .text = interpolate(step.text, variable_cells, value_cells) + }; + + if (step.data_table) { + ps.argument = cms::pickle_step_argument{ + .data_table = make_pickle_table( + *step.data_table, + variable_cells, + value_cells + ) + }; + } else if (step.doc_string) { + ps.argument = cms::pickle_step_argument{ + .doc_string = make_pickle_doc_string( + *step.doc_string, + variable_cells, + value_cells + ) + }; + } + + if (value_row_ptr) { + ps.ast_node_ids.push_back(value_row_ptr->id); + } + + return ps; +} + +cms::pickle_table +pickle_compiler::make_pickle_table( + const cms::data_table& dt, + const table_cells& variable_cells, + const table_cells& value_cells +) +{ + cms::pickle_table t; + + for (const auto& row : dt.rows) { + cms::pickle_table_row r; + + for (const auto& cell : row.cells) { + r.cells.emplace_back(cms::pickle_table_cell{ + .value = interpolate( + cell.value, + variable_cells, + value_cells + ) + }); + } + + t.rows.emplace_back(std::move(r)); + } + + return t; +} + +cms::pickle_doc_string +pickle_compiler::make_pickle_doc_string( + const cms::doc_string& ds, + const table_cells& variable_cells, + const table_cells& value_cells +) +{ + cms::pickle_doc_string pds{ + .content = interpolate(ds.content, variable_cells, value_cells) + }; + + if (ds.media_type) { + pds.media_type = interpolate( + *ds.media_type, + variable_cells, + value_cells + ); + } + + return pds; +} + +cms::pickle_step +pickle_compiler::make_pickle_step( + const cms::step& step, + cms::step_keyword_type keyword_type +) +{ return make_pickle_step(step, {}, nullptr, keyword_type); } + +pickle_tags +pickle_compiler::make_pickle_tags(const tags& tags) +{ + pickle_tags ptags; + + std::transform( + tags.cbegin(), tags.cend(), + std::back_inserter(ptags), + [](const auto& t) { + return + cms::pickle_tag{ + .name = t.name, + .ast_node_id = t.id + }; + } + ); + + return ptags; +} + +std::string +pickle_compiler::interpolate( + const std::string& name, + const table_cells& variable_cells, + const table_cells& value_cells +) +{ + auto iname = name; + std::size_t col = 0; + std::string header; + + for (const auto& variable_cell : variable_cells) { + const auto& value_cell = value_cells[col++]; + header = "<" + variable_cell.value + ">"; + + replace(iname, header, value_cell.value); + } + + return iname; +} + +std::string +pickle_compiler::next_id() +{ return idp_->next_id(); } + +} diff --git a/cpp/src/lib/gherkin/pickle_compiler_context.cpp b/cpp/src/lib/gherkin/pickle_compiler_context.cpp new file mode 100644 index 000000000..a7455ec70 --- /dev/null +++ b/cpp/src/lib/gherkin/pickle_compiler_context.cpp @@ -0,0 +1,19 @@ +#include + +namespace gherkin { + +std::string +pickle_compiler_context::next_id() +{ return idp->next_id(); } + +void +pickle_compiler_context::add_pickle(cms::pickle& p) +{ + if (sink) { + sink(p); + } + + pickles.emplace_back(std::move(p)); +} + +} diff --git a/cpp/src/lib/gherkin/regex.cpp b/cpp/src/lib/gherkin/regex.cpp new file mode 100644 index 000000000..a71a371c1 --- /dev/null +++ b/cpp/src/lib/gherkin/regex.cpp @@ -0,0 +1,56 @@ +#include + +namespace gherkin { + +void +split(const std::string& re, const std::string& expr, strings& list) +{ + list.clear(); + + if (expr.empty()) { + return; + } + + std::regex delim(re); + + auto cur = std::sregex_token_iterator( + expr.begin(), expr.end(), + delim, + -1 + ); + auto end = std::sregex_token_iterator(); + + for( ; cur != end; ++cur ) { + list.push_back(*cur); + } + + if (list.empty() && expr.size() > 0) { + list.push_back(expr); + } +} + +strings +split(const std::string& re, const std::string& expr) +{ + strings list; + + split(re, expr, list); + + return list; +} + +std::string +subst(const std::string& s, const std::string& re, const std::string& what) +{ + return std::regex_replace( + s, + std::regex(re), + what + ); +} + +void +subst(std::string& s, const std::string& re, const std::string& what) +{ s = subst(static_cast(s), re, what); } + +} diff --git a/cpp/src/lib/gherkin/rule_type.cpp b/cpp/src/lib/gherkin/rule_type.cpp new file mode 100644 index 000000000..4b7233414 --- /dev/null +++ b/cpp/src/lib/gherkin/rule_type.cpp @@ -0,0 +1,49 @@ +#include + +#include + +namespace gherkin { + +std::string_view +to_string(rule_type r) +{ + static const std::unordered_map rmap = { + { rule_type::none, "None" }, + { rule_type::e_o_f, "EOF" }, + { rule_type::empty, "Empty" }, + { rule_type::comment, "Comment" }, + { rule_type::tag_line, "TagLine" }, + { rule_type::feature_line, "FeatureLine" }, + { rule_type::rule_line, "RuleLine" }, + { rule_type::background_line, "BackgroundLine" }, + { rule_type::scenario_line, "ScenarioLine" }, + { rule_type::examples_line, "ExamplesLine" }, + { rule_type::step_line, "StepLine" }, + { rule_type::doc_string_separator, "DocStringSeparator" }, + { rule_type::table_row, "TableRow" }, + { rule_type::language, "Language" }, + { rule_type::other, "Other" }, + { rule_type::gherkin_document, "GherkinDocument" }, + { rule_type::feature, "Feature" }, + { rule_type::feature_header, "FeatureHeader" }, + { rule_type::rule, "Rule" }, + { rule_type::rule_header, "RuleHeader" }, + { rule_type::background, "Background" }, + { rule_type::scenario_definition, "ScenarioDefinition" }, + { rule_type::scenario, "Scenario" }, + { rule_type::examples_definition, "ExamplesDefinition" }, + { rule_type::examples, "Examples" }, + { rule_type::examples_table, "ExamplesTable" }, + { rule_type::step, "Step" }, + { rule_type::step_arg, "StepArg" }, + { rule_type::data_table, "DataTable" }, + { rule_type::doc_string, "DocString" }, + { rule_type::tags, "Tags" }, + { rule_type::description_helper, "DescriptionHelper" }, + { rule_type::description, "Description" } + }; + + return rmap.at(r); +} + +} diff --git a/cpp/src/lib/gherkin/token.cpp b/cpp/src/lib/gherkin/token.cpp new file mode 100644 index 000000000..8efd28137 --- /dev/null +++ b/cpp/src/lib/gherkin/token.cpp @@ -0,0 +1,17 @@ +#include + +namespace gherkin { + +bool +token::is_eof() const +{ return eof; } + +void +token::detach() +{} + +std::string_view +token::value() const +{ return is_eof() ? "EOF" : line.get_line_text(); } + +} diff --git a/cpp/src/lib/gherkin/token_formatter_builder.cpp b/cpp/src/lib/gherkin/token_formatter_builder.cpp new file mode 100644 index 000000000..179762809 --- /dev/null +++ b/cpp/src/lib/gherkin/token_formatter_builder.cpp @@ -0,0 +1,77 @@ +#include + +#include +#include + +namespace gherkin { + +token_formatter_builder::token_formatter_builder(id_generator_ptr idp) +: idp_(idp) +{} + +token_formatter_builder::~token_formatter_builder() +{} + +void +token_formatter_builder::reset(std::string_view uri) +{ + formatted_tokens_.clear(); +} + +void +token_formatter_builder::start_rule(rule_type rule_type) +{} + +void +token_formatter_builder::end_rule(rule_type rule_type) +{} + +void +token_formatter_builder::build(const token& token) +{ formatted_tokens_.emplace_back(format_token(token)); } + +strings +token_formatter_builder::get_result() const +{ return formatted_tokens_; } + +std::string +token_formatter_builder::format_token(const token& token) +{ + if (token.is_eof()) { + return "EOF"; + } + + std::ostringstream oss; + + oss + << "(" << token.location.line + << ":" << token.location.column.value_or(0) << ")" + << token.matched_type << ":" + ; + + if (token.matched_keyword) { + oss << "("; + + if (token.matched_keyword_type) { + oss << *token.matched_keyword_type; + } + + oss << ")" << token.matched_keyword.value(); + } + + oss << "/" << token.matched_text << "/"; + + if (!token.matched_items.empty()) { + strings items; + + for (const auto& i : token.matched_items) { + items.emplace_back(std::to_string(i.column) + ":" + i.text); + } + + oss << join(",", items); + } + + return oss.str(); +} + +} diff --git a/cpp/src/lib/gherkin/token_matcher.cpp b/cpp/src/lib/gherkin/token_matcher.cpp new file mode 100644 index 000000000..9d8928e3f --- /dev/null +++ b/cpp/src/lib/gherkin/token_matcher.cpp @@ -0,0 +1,405 @@ + +#include +#include +#include +#include +#include + +namespace gherkin { + +static const std::regex language_re{ + "^\\s*#\\s*language\\s*:\\s*([a-zA-Z\\-_]+)\\s*$" +}; + +token_matcher::token_matcher(const std::string& dialect_name) +{ + change_dialect(dialect_name); +} + +token_matcher::~token_matcher() +{} + +void +token_matcher::reset() +{ + indent_to_remove_ = 0; + active_doc_string_separator_.clear(); +} + +bool +token_matcher::match_feature_line(token& token) +{ + return + match_title_line( + token, + rule_type::feature_line, + keywords("feature") + ); +} + +bool +token_matcher::match_rule_line(token& token) +{ return match_title_line(token, rule_type::rule_line, keywords("rule")); } + +bool +token_matcher::match_scenario_line(token& token) +{ + auto rt = rule_type::scenario_line; + + return + match_title_line(token, rt, keywords("scenario")) + || + match_title_line(token, rt, keywords("scenarioOutline")) + ; +} + +bool +token_matcher::match_background_line(token& token) +{ + return + match_title_line( + token, + rule_type::background_line, + keywords("background") + ); +} + +bool +token_matcher::match_examples_line(token& token) +{ + return + match_title_line( + token, + rule_type::examples_line, + keywords("examples") + ); +} + +bool +token_matcher::match_table_row(token& token) +{ + if (!token.line.startswith("|")) { + return false; + } + + set_token_matched( + token, rule_type::table_row, { .items = token.line.table_cells() } + ); + + return true; +} + +bool +token_matcher::match_language(token& token) +{ + std::string dialect_name; + + if (!full_match(token.line.line_text(), language_re, dialect_name)) { + return false; + } + + set_token_matched(token, rule_type::language, { .text = dialect_name }); + change_dialect(dialect_name); + + return true; +} + +bool +token_matcher::match_tag_line(token& token) +{ + if (!token.line.startswith("@")) { + return false; + } + + set_token_matched( + token, rule_type::tag_line, { + .items = std::move(token.line.tags()) + } + ); + + return true; +} + +bool +token_matcher::match_title_line( + token& token, + rule_type token_type, + string_views keywords +) +{ + for (const auto& keyword : keywords) { + std::string k(keyword); + + if (!token.line.startswith_title_keyword(k)) { + continue; + } + + set_token_matched( + token, token_type, { + .text = token.line.get_keyword_trimmed(k), + .keyword = k + } + ); + + return true; + } + + return false; +} + +bool +token_matcher::match_e_o_f(token& token) +{ + if (!token.eof) { + return false; + } + + set_token_matched(token, rule_type::e_o_f); + + return true; +} + +bool +token_matcher::match_empty(token& token) +{ + if (!token.line.is_empty()) { + return false; + } + + set_token_matched(token, rule_type::empty, { .indent = 0 } ); + + return true; +} + +bool +token_matcher::match_comment(token& token) +{ + if (!token.line.startswith("#")) { + return false; + } + + auto comment_text = std::string(token.line.get_line_text(0)); + + subst(comment_text, "[\\r\\n]+$"); + + set_token_matched( + token, rule_type::comment, { + .text = comment_text, + .indent = 0 + } + ); + + return true; +} + +bool +token_matcher::match_other(token& token) +{ + std::string text = std::string(token.line.get_line_text(indent_to_remove_)); + + set_token_matched( + token, rule_type::other, { + .text = unescape_docstring(text), + .indent = 0 + } + ); + + return true; +} + +bool +token_matcher::match_step_line(token& token) +{ + string_views kws = { "given", "when", "then", "and", "but" }; + auto keywords = gherkin::keywords(dialect_name_, kws); + + for (const auto& keyword : keywords) { + if (!token.line.startswith(keyword)) { + continue; + } + + auto title = token.line.get_rest_trimmed(keyword.size()); + + set_token_matched( + token, rule_type::step_line, { + .text = title, + .keyword = std::string(keyword), + .keyword_type = keyword_type(keyword) + } + ); + + return true; + } + + return false; +} + +bool +token_matcher::match_doc_string_separator(token& token) +{ + if (active_doc_string_separator_.empty()) { + return + match_doc_string_separator_(token, "\"\"\"", true) + || + match_doc_string_separator_(token, "```", true); + } + + return + match_doc_string_separator_( + token, active_doc_string_separator_, false + ); +} + +bool +token_matcher::match_doc_string_separator_( + token& token, + std::string_view separator, + bool is_open +) +{ + if (!token.line.startswith(separator)) { + return false; + } + + std::string content_type; + std::string tseparator = std::string(separator); + + if (is_open) { + content_type = token.line.get_rest_trimmed(separator.size()); + active_doc_string_separator_ = tseparator; + indent_to_remove_ = token.line.indent(); + } else { + active_doc_string_separator_.clear(); + indent_to_remove_ = 0; + } + + set_token_matched( + token, rule_type::doc_string_separator, { + .text = content_type, + .keyword = tseparator + } + ); + + return true; +} + +void +token_matcher::set_token_matched( + token& token, + rule_type matched_type, + const token_info& ti +) +{ + using namespace std::literals; + + token.matched_type = matched_type; + + if (ti.text) { + token.matched_text.assign(rstrip(*ti.text, re_pattern::crlf)); + } + + if (ti.keyword) { + token.matched_keyword = *ti.keyword; + } + + if (ti.keyword_type) { + token.matched_keyword_type = *ti.keyword_type; + } + + if (ti.indent) { + token.matched_indent = *ti.indent; + } else { + token.matched_indent = token.line.indent(); + } + + token.matched_items = std::move(ti.items); + token.location.column = token.matched_indent + 1; + token.matched_gherkin_dialect = dialect_name_; +} + +const string_views& +token_matcher::keywords(std::string_view kw) const +{ return gherkin::keywords(dialect_name_, kw); } + +cucumber::messages::step_keyword_type +token_matcher::keyword_type(std::string_view keyword) const +{ + auto it = keyword_types_.find(keyword); + + if (it != keyword_types_.end()) { + const auto& kws = it->second; + + if (kws.size() == 1) { + return kws[0]; + } + } + + return cucumber::messages::step_keyword_type::UNKNOWN; +} + +void +token_matcher::change_dialect( + const std::string& dialect_name, + const cms::location& location +) +{ + if (all_keywords().find(dialect_name) == all_keywords().end()) { + throw no_such_language_error(dialect_name, location); + } + + dialect_name_ = dialect_name; + + auto d = get_dialect(dialect_name_); + + keyword_types_.clear(); + + for (const auto& keyword : d.given_keywords) { + keyword_types_[keyword].push_back( + cucumber::messages::step_keyword_type::CONTEXT + ); + } + + for (const auto& keyword : d.when_keywords) { + keyword_types_[keyword].push_back( + cucumber::messages::step_keyword_type::ACTION + ); + } + + for (const auto& keyword : d.then_keywords) { + keyword_types_[keyword].push_back( + cucumber::messages::step_keyword_type::OUTCOME + ); + } + + for (const auto& keyword : d.and_keywords) { + keyword_types_[keyword].push_back( + cucumber::messages::step_keyword_type::CONJUNCTION + ); + } + + for (const auto& keyword : d.but_keywords) { + keyword_types_[keyword].push_back( + cucumber::messages::step_keyword_type::CONJUNCTION + ); + } +} + +std::string +token_matcher::unescape_docstring(const std::string& text) const +{ + using namespace std::literals; + + std::string u; + + if (active_doc_string_separator_ == "\"\"\"") { + u = subst(text, "\\\\\"\\\\\"\\\\\"", "\"\"\""); + } else if (active_doc_string_separator_ == "```") { + u = subst(text, "\\\\`\\\\`\\\\`", "```"); + } else { + u = text; + } + + return u; +} + +} diff --git a/cpp/src/lib/gherkin/token_scanner.cpp b/cpp/src/lib/gherkin/token_scanner.cpp new file mode 100644 index 000000000..de90b59c8 --- /dev/null +++ b/cpp/src/lib/gherkin/token_scanner.cpp @@ -0,0 +1,81 @@ +#include +#include +#include + +#include +#include + +namespace gherkin { + +token_scanner::token_scanner() +{} + +token_scanner::token_scanner(std::string_view data) +{ reset(data); } + +token_scanner::token_scanner(const file& file) +{ reset(file); } + +token_scanner::~token_scanner() +{} + +token +token_scanner::read() +{ + auto r = next_line(); + + line_++; + + return token{ + .eof = r.eof, + .line = gherkin::line(r.text, line_), + .location = { + .line = line_ + } + }; +} + +void +token_scanner::reset() +{ + line_ = 0; +} + +void +token_scanner::reset(std::string_view data) +{ + reset(); + ip_ = std::make_unique(std::string(data)); +} + +void +token_scanner::reset(const file& file) +{ + reset(); + ip_ = std::make_unique(file.path); +} + + +next_line_result +token_scanner::next_line() +{ + next_line_result r; + std::string line; + + if (ip_) { + if (!input().eof()) { + r.eof = !std::getline(input(), line); + r.text = rstrip(line, re_pattern::cr); + } else { + ip_.reset(); + } + } + + return r; +} + +std::istream& +token_scanner::input() +{ return *ip_; } + +} diff --git a/cpp/src/lib/gherkin/utils.cpp b/cpp/src/lib/gherkin/utils.cpp new file mode 100644 index 000000000..3b827a2c5 --- /dev/null +++ b/cpp/src/lib/gherkin/utils.cpp @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include + +#include + +namespace gherkin { + +std::string +slurp(const std::string& path) +{ + namespace fs = std::filesystem; + + std::string bytes; + std::ifstream ifs(path, std::ios::binary); + + if (ifs.is_open()) { + auto fsize = fs::file_size(fs::path{ path }); + + bytes.resize(fsize); + + ifs.read(bytes.data(), fsize); + ifs.close(); + } + + return bytes; +} + +void +replace(std::string& s, std::string_view what, std::string_view with) +{ + std::string::size_type pos; + + while ((pos = s.find(what)) != std::string::npos) { + s.replace(pos, what.size(), with); + } +} + +std::string +replace(const std::string& s, std::string_view what, std::string_view with) +{ + std::string t = s; + + replace(t, what, with); + + return t; +} + +}