Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: cucumber/gherkin
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 56f3f685dd0f8d5ccf02896a39b10c6e1af141d4
Choose a base ref
..
head repository: cucumber/gherkin
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: c95ac3384ac9930728dff0f05f40c23835dedb55
Choose a head ref
Showing with 71 additions and 54 deletions.
  1. +71 −54 README.md
125 changes: 71 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
@@ -4,18 +4,18 @@ Gherkin is a parser and compiler for the Gherkin language.

Gherkin is currently implemented for the following platforms (in order of birthday):

- [.NET](./dotnet) - Actively tested -> [workflow](./.github/workflows/test-dotnet.yml)
- [Java](./java) - Actively tested -> [workflow](./.github/workflows/test-java.yml)
- [JavaScript](./javascript) - Actively tested -> [workflow](./.github/workflows/test-javascript.yml)
- [Ruby](./ruby) - Actively tested -> [workflow](./.github/workflows/test-ruby.yml)
- [Go](./go) - Actively tested -> [workflow](./.github/workflows/test-go.yml)
- [Python](./python) - Actively tested -> [workflow](./.github/workflows/test-python.yml)
- [C](./c) - Actively tested -> [workflow](./.github/workflows/test-c.yml)
- [.NET](./dotnet) - [![test dotnet workflow](https://github.com/cucumber/gherkin/actions/workflows/test-dotnet.yml/badge.svg)](./.github/workflows/test-dotnet.yml)
- [Java](./java) - [![test java workflow](https://github.com/cucumber/gherkin/actions/workflows/test-java.yml/badge.svg)](./.github/workflows/test-java.yml)
- [JavaScript](./javascript) - [![test javascript workflow](https://github.com/cucumber/gherkin/actions/workflows/test-javascript.yml/badge.svg)](./.github/workflows/test-javascript.yml)
- [Ruby](./ruby) - [![test ruby workflow](https://github.com/cucumber/gherkin/actions/workflows/test-ruby.yml/badge.svg)](./.github/workflows/test-ruby.yml)
- [Go](./go) - [![test go workflow](https://github.com/cucumber/gherkin/actions/workflows/test-go.yml/badge.svg)](./.github/workflows/test-go.yml)
- [Python](./python) - [![test python workflow](https://github.com/cucumber/gherkin/actions/workflows/test-python.yml/badge.svg)](./.github/workflows/test-python.yml)
- [C](./c) - [![test c workflow](https://github.com/cucumber/gherkin/actions/workflows/test-c.yml/badge.svg)](./.github/workflows/test-c.yml)
- [Objective-C](./objective-c) - _Currently not actively tested, requires maintenance_
- [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)
- [Perl](./perl) - [![test perl workflow](https://github.com/cucumber/gherkin/actions/workflows/test-perl.yml/badge.svg)](./.github/workflows/test-perl.yml)
- [PHP](./php) - [![test php workflow](https://github.com/cucumber/gherkin/actions/workflows/test-php.yml/badge.svg)](./.github/workflows/test-php.yml)
- [Dart](./dart) - [![test dart workflow](https://github.com/cucumber/gherkin/actions/workflows/test-dart.yml/badge.svg)](./.github/workflows/test-dart.yml)
- [C++](./cpp) - [![test cpp workflow](https://github.com/cucumber/gherkin/actions/workflows/test-cpp.yml/badge.svg)](./.github/workflows/test-cpp.yml)

The CI will run using the linked workflow when that specific language implementation is changed

@@ -44,7 +44,7 @@ Gherkin can be used either through its command line interface (CLI) or as a
library.

It is designed to be used in conjunction with other tools such as Cucumber
which consumes the output from the CLI or library as [Cucumber Messages](../messages).
which consumes the output from the CLI or library as [Cucumber Messages](https://github.com/cucumber/messages).

### Library

@@ -58,21 +58,24 @@ that is easy to use. See the CLI implementations to get an idea of how to use it

Alternatively, you can use the lower level parser and compiler. Some usage examples are below:

#### Java

```java
// Java
Path path = Paths.get("../testdata/good/minimal.feature");
GherkinParser parser = GherkinParser.builder().build();
Stream<Envelope> pickles = parser.parse(envelope).filter(envelope -> envelope.getPickle().isPresent());
```

#### C#

```csharp
// C#
var parser = new Parser();
var gherkinDocument = parser.Parse(@"Drive:\PathToGherkinDocument\document.feature");
```

#### Ruby

```ruby
# Ruby
require 'gherkin/parser'
require 'gherkin/pickles/compiler'

@@ -88,22 +91,28 @@ id_generator = Cucumber::Messages::IdGenerator::UUID.new
pickles = Gherkin::Pickles::Compiler.new(id_generator).compile(gherkin_document, source)
```

```javascript
// JavaScript
var Gherkin = require('@cucumber/gherkin')
var Messages = require('@cucumber/messages')

var uuidFn = Messages.IdGenerator.uuid()
var builder = new Gherkin.AstBuilder(uuidFn)
var matcher = new Gherkin.GherkinClassicTokenMatcher() // or Gherkin.GherkinInMarkdownTokenMatcher()
#### JavaScript

var parser = new Gherkin.Parser(builder, matcher)
var gherkinDocument = parser.parse('Feature: ...')
var pickles = Gherkin.compile(gherkinDocument, 'uri_of_the_feature.feature', uuidFn)
```javascript
var Gherkin = require("@cucumber/gherkin");
var Messages = require("@cucumber/messages");

var uuidFn = Messages.IdGenerator.uuid();
var builder = new Gherkin.AstBuilder(uuidFn);
var matcher = new Gherkin.GherkinClassicTokenMatcher(); // or Gherkin.GherkinInMarkdownTokenMatcher()

var parser = new Gherkin.Parser(builder, matcher);
var gherkinDocument = parser.parse("Feature: ...");
var pickles = Gherkin.compile(
gherkinDocument,
"uri_of_the_feature.feature",
uuidFn
);
```

#### Go

```go
// Go
// Download the package via: `go get github.com/cucumber/gherkin/go/v27`
// && go get "github.com/cucumber/messages/go/v22"
import (
@@ -121,8 +130,9 @@ func main() {
}
```

#### Python

```python
# Python
from gherkin.parser import Parser
from gherkin.pickles.compiler import Compiler

@@ -132,8 +142,9 @@ gherkin_document["uri"] = "uri_of_the_feature.feature"
pickles = Compiler().compile(gherkin_document)
```

#### Objective-C

```Objective-C
// Objective-C
#import "GHParser+Extensions.h"

GHParser * parser = [[GHParser alloc] init];
@@ -145,8 +156,9 @@ if([content stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewline
GHGherkinDocument * result = [parser parseContent:content];
```
#### Perl
```perl
# Perl
use Gherkin::Parser;
use Gherkin::Pickles::Compiler;
@@ -155,8 +167,9 @@ my $gherkin_document = $parser->parse("Feature: ...");
my $pickles = Gherkin::Pickles::Compiler->compile($gherkin_document);
```

#### PHP

```php
# PHP
use Cucumber\Gherkin\GherkinParser;

$path = '/path/to/my.feature';
@@ -168,22 +181,26 @@ $pickles = $parser->parseString(uri: $path, data: file_get_contents($path));
### CLI

The Gherkin CLI `gherkin` reads Gherkin source files (`.feature` files) and outputs
[ASTs](#ast) and [Pickles](#pickles).
[ASTs](#abstract-syntax-tree-ast) and [Pickles](#pickles).

The `gherkin` program takes any number of files as arguments and prints the results
to `STDOUT` as [Newline Delimited JSON](http://ndjson.org/).
to `STDOUT` as [Newline Delimited JSON](https://jsonlines.org).

Each line is a JSON document that conforms to the [Cucumber Event Protocol](../messages).
Each line is a JSON document that conforms to the [Cucumber Event Protocol](https://github.com/cucumber/messages).

To try it out, just install Gherkin for your favourite language, and run it over the
files in this repository:

gherkin testdata/**/*.feature
```console
gherkin testdata/**/*.feature
```

Ndjson is easy to read for programs, but hard for people. To pretty print each JSON
document you can pipe it to the [jq](https://stedolan.github.io/jq/) program:

gherkin testdata/**/*.feature | jq
```console
gherkin testdata/**/*.feature | jq
```

## Table cell escaping

@@ -195,9 +212,11 @@ finally, if you need a `\`, you can escape that with `\\`.

The following diagram outlines the architecture:

╔════════════╗ ┌───────┐ ╔══════╗ ┌──────┐ ╔═══╗
║Feature file║──>│Scanner│──>║Tokens║──>│Parser│──>║AST║
╚════════════╝ └───────┘ ╚══════╝ └──────┘ ╚═══╝
```mermaid
graph LR
A[Feature file] -->|Scanner| B[Tokens]
B -->|Parser| D[AST]
```

The _scanner_ reads a gherkin doc (typically read from a `.feature` file) and creates
a _token_ for each line. The tokens are passed to the _parser_, which outputs an _AST_
@@ -213,17 +232,12 @@ parser generator as part of the build process.
Berp takes a grammar file (`gherkin.berp`) and a template file (`gherkin-X.razor`) as input
and outputs a parser in language _X_:

╔════════════╗ ┌────────┐ ╔═══════════════╗
║gherkin.berp║──>│berp.exe│<──║gherkin-X.razor║
╚════════════╝ └────────┘ ╚═══════════════╝
V
╔════════╗
║Parser.x║
╚════════╝

Also see the [wiki](https://github.com/cucumber/gherkin/wiki) for some early
design docs (which might be a little outdated, but mostly OK).
```mermaid
graph TD
A[gherkin.berp] --> B[berp.exe]
C[gherkin-X.razor] --> B
B --> D[Parser.x]
```

### Abstract Syntax Tree (AST)

@@ -326,9 +340,10 @@ into a simpler form called _Pickles_.

The compiler compiles the AST produced by the parser into pickles:

╔═══╗ ┌────────┐ ╔═══════╗
║AST║──>│Compiler│──>║Pickles║
╚═══╝ └────────┘ ╚═══════╝
```mermaid
graph LR
A[AST] -->|Compiler| B[Pickles]
```

The rationale is to decouple Gherkin from Cucumber so that Cucumber is open to
support alternative formats to Gherkin (for example Markdown).
@@ -373,7 +388,9 @@ Feature:

Using the [CLI](#cli) we can compile this into several pickle objects:

gherkin testdata/good/readme_example.feature --no-source --no-ast | jq
```console
gherkin testdata/good/readme_example.feature --no-source --no-ast | jq
```

Output: