Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C++ implementation #117

Closed
wants to merge 79 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
b55b170
chore: adding cpp implementation
Apr 28, 2023
70a41a3
chore: i18n OK
Apr 29, 2023
216c593
chore: more implementation
Apr 30, 2023
09de7e1
feat: CMake build
May 1, 2023
7cad613
chore: cleanup up a bit
May 2, 2023
c10fb71
chore: compile ok
May 2, 2023
3922ccd
chore: more implementation
May 2, 2023
e710ebe
chore: added rudimentary source dependency installer
May 4, 2023
ef0e6c0
chore: added external dependency support
May 4, 2023
eb83391
chore: more token_matcher implementation
May 4, 2023
56c2245
chore: ast
May 5, 2023
bf1e962
chore: added some utilities
May 5, 2023
b563dba
chore: trying to have a clean ast_node construction
May 5, 2023
ba9be9e
chore: integrated types from cucumber::messages library
May 6, 2023
1e73ad6
chore: trying different ast_node sub item management
May 6, 2023
9def3c8
chore: more ast_builder implementation
May 7, 2023
3ddd159
chore: cleaned ast_node tree implementation
May 8, 2023
6eec054
chore: most of ast_builder done
May 9, 2023
0cce844
chore: adding binaries
May 9, 2023
d9e5681
chore: bug fixes and gearing towards testing
May 10, 2023
06bb4c0
chore: cleaned node visitation
May 11, 2023
4ba3527
chore: cleaned optional implementation
May 12, 2023
94daced
chore: refactoring ast_node
May 14, 2023
dfb58f8
chore: properly reset scanner
May 15, 2023
32db8e8
fixed parser workflow
May 15, 2023
0e9e74b
chore: improving parser interface
May 15, 2023
195943e
chore: cleaned implementation with parser_base base class
May 16, 2023
284c158
chore: implementing pickles compiler
May 16, 2023
a1ceef7
chore: pickle compiler
May 17, 2023
06fa08c
chore: refactored parser hierarchy
May 18, 2023
eaac12b
chore: first feature tokens ok
May 22, 2023
6cacb9a
Merge branch 'cucumber:main' into feature/cpp
chybz May 23, 2023
eb19c80
chore: token_matcher and formatter fixes
May 23, 2023
87223a6
chore: regex and language detection fixes
May 23, 2023
2c790dc
fix: proper eof condition
May 23, 2023
d926768
chore: fixed stupid string_view overwrite
May 23, 2023
cb6ba14
chore: handling utf8 string size
May 23, 2023
2d42bfb
chore: handling multibyte string code points length
May 24, 2023
8d1cff9
chore: fixing mutlibyte handling
May 24, 2023
3678767
fixing utf8
May 25, 2023
33de481
chore: codepoint and comment carriage return fixes
May 26, 2023
874fac2
chore: fixing some more utf8 stuff
May 26, 2023
1e1018f
chore: wide-narrow conversion fixes
May 27, 2023
d3290e1
chore: tokens acceptance OK
May 27, 2023
e1f1e75
chore: improved api, fixed json output
May 28, 2023
5879e75
chore: fixed gherkinDocument root key name and missing feature language
May 29, 2023
441c7ff
chore: added small debug helper
May 29, 2023
ee3856f
chore: ast json output acceptance ok
May 29, 2023
6031def
chore: fixing pickle step interpolation
May 29, 2023
16999ac
chore: fixing error reporting
May 30, 2023
0f45c53
chore: fixing error reporting
May 30, 2023
994b39f
feat: ACCEPTANCE OK (phewww)
May 31, 2023
e46b515
chore: added nlohmann::json dependency
May 31, 2023
825bd8c
chore: added introductory documentation
May 31, 2023
a7cccc8
Merge branch 'cucumber:main' into feature/cpp
chybz May 31, 2023
f7b2b38
Update README.md
chybz May 31, 2023
848b6b8
Update CHANGELOG.md
chybz May 31, 2023
35ad8be
chore: use main cucumber/messages repository, fix workflow name
Jun 1, 2023
57d5751
chore: increasing default step timeout
Jun 1, 2023
2d44e52
chore: limiting parallelism
Jun 1, 2023
2fab131
chore: setting NPROCS environment variable
Jun 1, 2023
1284f81
chore: debugging workflow
Jun 1, 2023
8a98ddb
chore: debugging workflow
Jun 1, 2023
6992857
chore: debugging workflow
Jun 1, 2023
c2aed42
chore: debugging workflow
Jun 1, 2023
b310b9a
chore: generate files int their own directory
Jun 1, 2023
e65778a
fix: don't delete parent files...
Jun 1, 2023
ce49fe9
fix: clean targets
Jun 1, 2023
3ba4c39
fix: don't delete dialect.cpp
Jun 1, 2023
c4f175f
chore: loosing my mind...
Jun 1, 2023
f8d5b16
chore: enough for today...
Jun 1, 2023
bda6c35
chore: hopefully fixed makefile and generation
Jun 2, 2023
ca78995
chore: getting rid of spurious generation. Attempt 1
Jun 2, 2023
6aebf1d
chore: getting rid of spurious generation. Attempt 2
Jun 2, 2023
dcd1cf0
chore: getting rid of spurious generation. Attempt 3
Jun 2, 2023
5683fe5
chore: getting rid of spurious generation. Attempt 4
Jun 2, 2023
a7750f8
fix: correct generated source file filtering
Jun 2, 2023
9c6bfee
chore: lowered to C++17 standard, added basic README
Jun 2, 2023
4df54e7
Merge branch 'cucumber:main' into feature/cpp
chybz Jun 2, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/test-cpp.yml
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](http://semver.org).
This document is formatted according to the principles of [Keep A CHANGELOG](http://keepachangelog.com).

## [Unreleased]
### Added
- C++ implementation [#117](https://github.com/cucumber/gherkin/pull/117)

## [26.2.0] - 2023-04-07
### Changed
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
languages = c dart dotnet elixir go java javascript perl php python ruby
languages = c cpp dart dotnet elixir go java javascript perl php python ruby

.DEFAULT_GOAL = help

Expand Down Expand Up @@ -59,4 +59,4 @@ docker-run: ## Start a docker container with all languages and tools installed
--tty \
cucumber/cucumber-build:0.13.0 \
bash
.PHONY:
.PHONY:
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Gherkin is currently implemented for the following platforms (in order of birthd
- [Objective-C](./objective-c)
- [Perl](./perl)
- [PHP](./php)
- [C++](./cpp)


## Contributing Translations (i18n)
Expand Down Expand Up @@ -150,6 +151,27 @@ $parser = new GherkinParser();
$pickles = $parser->parseString(uri: $path, data: file_get_contents($path));
```

```c++
// C++
#include <gherkin/parser.hpp>
#include <gherkin/pickle_compiler.hpp>
#include <gherkin/utils.hpp>

int main(int ac, char** av)
{
gherkin::parser<> p;
std::string feature_file{ av[1] };

auto ast = p.parse(feature_file, gherkin::slurp(feature_file));

gherkin::pickle_compiler pc;

auto pickles = pc.compile(ast, feature_file);

return 0;
}
```

### CLI

The Gherkin CLI `gherkin` reads Gherkin source files (`.feature` files) and outputs
Expand Down
7 changes: 7 additions & 0 deletions cpp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ext/
build/
acceptance/
stage/
.built
.configured
.deps-installed
40 changes: 40 additions & 0 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
)
121 changes: 121 additions & 0 deletions cpp/Makefile
Original file line number Diff line number Diff line change
@@ -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 <target>\n\nWhere <target> 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
13 changes: 13 additions & 0 deletions cpp/README.md
Original file line number Diff line number Diff line change
@@ -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 <project_root>/cpp
make generate-all
~~~
18 changes: 18 additions & 0 deletions cpp/cmake/toolchains/ext.cmake
Original file line number Diff line number Diff line change
@@ -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()
2 changes: 2 additions & 0 deletions cpp/deps.txt
Original file line number Diff line number Diff line change
@@ -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
44 changes: 44 additions & 0 deletions cpp/dialect.cpp.jq
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
. as $root |
[
"#include <gherkin/dialect.hpp>\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
Loading