{"id":14969269,"url":"https://github.com/cucumber/gherkin","last_synced_at":"2025-05-14T05:11:40.245Z","repository":{"id":62554628,"uuid":"550879823","full_name":"cucumber/gherkin","owner":"cucumber","description":"A parser and compiler for the Gherkin language.","archived":false,"fork":false,"pushed_at":"2025-05-10T01:12:15.000Z","size":11082,"stargazers_count":225,"open_issues_count":49,"forks_count":72,"subscribers_count":101,"default_branch":"main","last_synced_at":"2025-05-12T20:27:18.984Z","etag":null,"topics":["c","dart","dotnet","elixir","gherkin","go","java","javascript","lexer","objective-c","parser","perl","php","polyglot-release","python","ruby","tidelift"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cucumber.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"open_collective":"cucumber","github":"cucumber"}},"created_at":"2022-10-13T13:28:45.000Z","updated_at":"2025-05-10T15:31:13.000Z","dependencies_parsed_at":"2023-12-26T14:32:40.645Z","dependency_job_id":"3d4258ed-7942-41fd-964a-b901646be052","html_url":"https://github.com/cucumber/gherkin","commit_stats":{"total_commits":2487,"total_committers":128,"mean_commits":19.4296875,"dds":0.63610776035384,"last_synced_commit":"4dc94d931e6f6a5c3cd1f69ad878719d05407952"},"previous_names":[],"tags_count":109,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cucumber%2Fgherkin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cucumber%2Fgherkin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cucumber%2Fgherkin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cucumber%2Fgherkin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cucumber","download_url":"https://codeload.github.com/cucumber/gherkin/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254076850,"owners_count":22010611,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","dart","dotnet","elixir","gherkin","go","java","javascript","lexer","objective-c","parser","perl","php","polyglot-release","python","ruby","tidelift"],"created_at":"2024-09-24T13:41:28.040Z","updated_at":"2025-05-14T05:11:40.197Z","avatar_url":"https://github.com/cucumber.png","language":"C","funding_links":["https://opencollective.com/cucumber","https://github.com/sponsors/cucumber"],"categories":[],"sub_categories":[],"readme":"# Gherkin\n\nGherkin is a parser and compiler for the Gherkin language.\n\nGherkin is currently implemented for the following platforms (in order of birthday):\n\n- [.NET](./dotnet) - [![test dotnet workflow](https://github.com/cucumber/gherkin/actions/workflows/test-dotnet.yml/badge.svg)](./.github/workflows/test-dotnet.yml)\n- [Java](./java) - [![test java workflow](https://github.com/cucumber/gherkin/actions/workflows/test-java.yml/badge.svg)](./.github/workflows/test-java.yml)\n- [JavaScript](./javascript) - [![test javascript workflow](https://github.com/cucumber/gherkin/actions/workflows/test-javascript.yml/badge.svg)](./.github/workflows/test-javascript.yml)\n- [Ruby](./ruby) - [![test ruby workflow](https://github.com/cucumber/gherkin/actions/workflows/test-ruby.yml/badge.svg)](./.github/workflows/test-ruby.yml)\n- [Go](./go) - [![test go workflow](https://github.com/cucumber/gherkin/actions/workflows/test-go.yml/badge.svg)](./.github/workflows/test-go.yml)\n- [Python](./python) - [![test python workflow](https://github.com/cucumber/gherkin/actions/workflows/test-python.yml/badge.svg)](./.github/workflows/test-python.yml)\n- [C](./c) - [![test c workflow](https://github.com/cucumber/gherkin/actions/workflows/test-c.yml/badge.svg)](./.github/workflows/test-c.yml)\n- [Objective-C](./objective-c) - _Currently not actively tested, requires maintenance_\n- [Perl](./perl) - [![test perl workflow](https://github.com/cucumber/gherkin/actions/workflows/test-perl.yml/badge.svg)](./.github/workflows/test-perl.yml)\n- [PHP](./php) - [![test php workflow](https://github.com/cucumber/gherkin/actions/workflows/test-php.yml/badge.svg)](./.github/workflows/test-php.yml)\n- [Dart](./dart) - [![test dart workflow](https://github.com/cucumber/gherkin/actions/workflows/test-dart.yml/badge.svg)](./.github/workflows/test-dart.yml)\n- [C++](./cpp) - [![test cpp workflow](https://github.com/cucumber/gherkin/actions/workflows/test-cpp.yml/badge.svg)](./.github/workflows/test-cpp.yml)\n\nThe CI will run using the linked workflow when that specific language implementation is changed\n\nThe CI will also run for any/all linked workflows when any [test data](./testdata) is modified\n(For example modifying one of the good or bad features / ndjson outputs)\n\n## Contributing Translations (i18n)\n\nIn order to allow Gherkin to be written in a number of languages, the keywords\nhave been translated into multiple languages. To improve readability and flow,\nsome languages may have more than one translation for any given keyword.\n\nIf you are looking to add, update or improve these translations please see\n[`CONTRIBUTING.md`](CONTRIBUTING.md#adding-or-updating-an-i18n-language).\n\n## Contributing a Parser Implementation\n\nSee [`CONTRIBUTING.md`](CONTRIBUTING.md) if you want to contribute a parser\nfor a new programming language. Our wish-list is (in no particular order):\n\n- Rust\n\n## Usage\n\nGherkin can be used either through its command line interface (CLI) or as a\nlibrary.\n\nIt is designed to be used in conjunction with other tools such as Cucumber\nwhich consumes the output from the CLI or library as [Cucumber Messages](https://github.com/cucumber/messages).\n\n### Library\n\nUsing the library is the preferred way to use Gherkin since it produces easily\nconsumable AST and Pickle objects in-process without having to fork a CLI process\nor parse JSON.\n\nThe library itself provides a _stream_ API, which is what the CLI is based on.\nThis is the recommended way to use the library as it provides a high level API\nthat is easy to use. See the CLI implementations to get an idea of how to use it.\n\nAlternatively, you can use the lower level parser and compiler. Some usage examples are below:\n\n#### Java\n\n```java\nPath path = Paths.get(\"../testdata/good/minimal.feature\");\nGherkinParser parser = GherkinParser.builder().build();\nStream\u003cEnvelope\u003e pickles = parser.parse(envelope).filter(envelope -\u003e envelope.getPickle().isPresent());\n```\n\n#### C#\n\n```csharp\nvar parser = new Parser();\nvar gherkinDocument = parser.Parse(@\"Drive:\\PathToGherkinDocument\\document.feature\");\n```\n\n#### Ruby\n\n```ruby\nrequire 'gherkin/parser'\nrequire 'gherkin/pickles/compiler'\n\nsource = {\n  uri: 'uri_of_the_feature.feature',\n  data: 'Feature: ...',\n  mediaType: 'text/x.cucumber.gherkin+plain'\n}\n\ngherkin_document = Gherkin::Parser.new.parse(source[:data])\nid_generator = Cucumber::Messages::IdGenerator::UUID.new\n\npickles = Gherkin::Pickles::Compiler.new(id_generator).compile(gherkin_document, source)\n```\n\n#### JavaScript\n\n```javascript\nvar Gherkin = require(\"@cucumber/gherkin\");\nvar Messages = require(\"@cucumber/messages\");\n\nvar uuidFn = Messages.IdGenerator.uuid();\nvar builder = new Gherkin.AstBuilder(uuidFn);\nvar matcher = new Gherkin.GherkinClassicTokenMatcher(); // or Gherkin.GherkinInMarkdownTokenMatcher()\n\nvar parser = new Gherkin.Parser(builder, matcher);\nvar gherkinDocument = parser.parse(\"Feature: ...\");\nvar pickles = Gherkin.compile(\n  gherkinDocument,\n  \"uri_of_the_feature.feature\",\n  uuidFn\n);\n```\n\n#### Go\n\n```go\n// Download the package via: `go get github.com/cucumber/gherkin/go/v27`\n//   \u0026\u0026 go get \"github.com/cucumber/messages/go/v22\"\nimport (\n  \"strings\"\n\n  gherkin \"github.com/cucumber/gherkin/go/v27\"\n  messages \"github.com/cucumber/messages/go/v22\"\n)\n\nfunc main() {\n  uuid := \u0026message.UUID{} // or \u0026message.Incrementing{}\n  reader := strings.NewReader(`Feature: ...`)\n  gherkinDocument, err := gherkin.ParseGherkinDocument(reader, uuid.NewId)\n  pickles := gherkin.Pickles(*gherkinDocument, \"minimal.feature\", uuid.NewId)\n}\n```\n\n#### Python\n\n```python\nfrom gherkin import Compiler, Parser\n\ngherkin_document = Parser().parse(\"Feature: ...\")\ngherkin_document[\"uri\"] = \"uri_of_the_feature.feature\"\npickles = Compiler().compile(gherkin_document)\n```\n\n#### Objective-C\n\n```Objective-C\n#import \"GHParser+Extensions.h\"\n\nGHParser * parser = [[GHParser alloc] init];\nNSString * featureFilePath; // Should refer to the place where we can get the content of the feature\nNSString * content = [NSString stringWithContentsOfURL:featureFilePath encoding:NSUTF8StringEncoding error:nil];\nif([content stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length == 0){\n      // GHParser will throw an error if you passed empty content... handle this issue first.\n}\nGHGherkinDocument * result = [parser parseContent:content];\n```\n\n#### Perl\n\n```perl\nuse Gherkin::Parser;\nuse Gherkin::Pickles::Compiler;\n\nmy $parser = Gherkin::Parser-\u003enew();\nmy $gherkin_document = $parser-\u003eparse(\"Feature: ...\");\nmy $pickles = Gherkin::Pickles::Compiler-\u003ecompile($gherkin_document);\n```\n\n#### PHP\n\n```php\nuse Cucumber\\Gherkin\\GherkinParser;\n\n$path = '/path/to/my.feature';\n\n$parser = new GherkinParser();\n$pickles = $parser-\u003eparseString(uri: $path, data: file_get_contents($path));\n```\n\n### CLI\n\nThe Gherkin CLI `gherkin` reads Gherkin source files (`.feature` files) and outputs\n[ASTs](#abstract-syntax-tree-ast) and [Pickles](#pickles).\n\nThe `gherkin` program takes any number of files as arguments and prints the results\nto `STDOUT` as [Newline Delimited JSON](https://jsonlines.org).\n\nEach line is a JSON document that conforms to the [Cucumber Event Protocol](https://github.com/cucumber/messages).\n\nTo try it out, just install Gherkin for your favourite language, and run it over the\nfiles in this repository:\n\n```console\ngherkin testdata/**/*.feature\n```\n\nNdjson is easy to read for programs, but hard for people. To pretty print each JSON\ndocument you can pipe it to the [jq](https://stedolan.github.io/jq/) program:\n\n```console\ngherkin testdata/**/*.feature | jq\n```\n\n## Table cell escaping\n\nIf you want to use a newline character in a table cell, you can write this\nas `\\n`. If you need a `|` as part of the cell, you can escape it as `\\|`. And\nfinally, if you need a `\\`, you can escape that with `\\\\`.\n\n## Architecture\n\nThe following diagram outlines the architecture:\n\n```mermaid\ngraph LR\n    A[Feature file] --\u003e|Scanner| B[Tokens]\n    B --\u003e|Parser| D[AST]\n```\n\nThe _scanner_ reads a gherkin doc (typically read from a `.feature` file) and creates\na _token_ for each line. The tokens are passed to the _parser_, which outputs an _AST_\n(Abstract Syntax Tree).\n\nIf the scanner sees a `#language` header, it will reconfigure itself dynamically\nto look for Gherkin keywords for the associated language. The keywords are defined in\n`gherkin-languages.json`.\n\nThe scanner is hand-written, but the parser is generated by the [Berp](https://github.com/gasparnagy/berp)\nparser generator as part of the build process.\n\nBerp takes a grammar file (`gherkin.berp`) and a template file (`gherkin-X.razor`) as input\nand outputs a parser in language _X_:\n\n```mermaid\ngraph TD\n    A[gherkin.berp] --\u003e B[berp.exe]\n    C[gherkin-X.razor] --\u003e B\n    B --\u003e D[Parser.x]\n```\n\n### Abstract Syntax Tree (AST)\n\nThe AST produced by the parser can be described with the following class diagram:\n\n```mermaid\nclassDiagram\n    ScenarioOutline --|\u003e ScenarioDefinition\n    GherkinDocument \"1\" *-- \"0..1\" Comment: comment\n    GherkinDocument \"1\" *-- \"0..1\" Feature: feature\n    Feature \"1\" *-- \"0..*\" ScenarioDefinition: scenarioDefinitions\n    Feature \"1\" *-- \"0..*\" Rule: rules\n    Rule \"1\" *-- \"0..*\" ScenarioDefinition: scenarioDefinitions\n    Background \"0..1\" --* \"1\" Rule: background\n    Feature \"1\" *-- \"0..1\" Background: background\n    Scenario --|\u003e ScenarioDefinition\n    Tag \"0..*\" --* \"1\" Feature: tags\n    Tag \"0..*\" --* \"1\" Rule: tags\n    Tag \"0..*\" --* \"1\" Scenario: tags\n    Tag \"0..*\" --* \"1\" ScenarioOutline: tags\n    Tag \"0..*\" --* \"1\" Examples: tags\n    Examples \"0..*\" --* \"1\" ScenarioOutline: examples\n    TableRow \"1\" --* \"1\" Examples: header\n    TableRow \"0..*\" --* \"1\" Examples: rows\n    Background \"1\" *-- \"0..*\" Step: steps\n    Step \"0..*\" --* \"1\" ScenarioDefinition: steps\n    StepArgument \"0..1\" --* \"1\" Step: stepArgument\n    DataTable --|\u003e StepArgument\n    StepArgument \u003c|-- DocString\n    TableRow \"0..*\" --* \"1\" DataTable: rows\n    TableRow \"1\" *-- \"0..*\" TableCell: cells\n    class ScenarioDefinition {\n        keyword\n        name\n        description\n    }\n    class Step {\n        keyword\n        text\n    }\n    class Examples {\n        keyword\n        name\n        description\n    }\n    class Feature {\n        language\n        keyword\n        name\n        description\n    }\n    class Background {\n        keyword\n        name\n        description\n    }\n    class Rule {\n        keyword\n        name\n        description\n    }\n    class DocString {\n        content\n        contentType\n    }\n    class Comment {\n        text\n    }\n    class TableCell {\n        value\n    }\n    class Tag {\n        name\n    }\n    class Location {\n        line: int\n        column: int\n    }\n```\n\nEvery class represents a node in the AST. Every node has a `Location` that describes\nthe line number and column number in the input file. These numbers are 1-indexed.\n\nAll fields on nodes are strings (except for `Location.line` and `Location.column`).\n\nThe implementation is simple objects without behaviour, only data. It's up to\nthe implementation to decide whether to use classes or just basic collections,\nbut the AST _must_ have a JSON representation (this is used for testing).\n\nEach node in the JSON representation also has a `type` property with the name\nof the node type.\n\nYou can see some examples in the\n[testdata/good](https://github.com/cucumber/gherkin/tree/main/testdata/good)\ndirectory.\n\n### Pickles\n\nThe AST isn't suitable for execution by Cucumber. It needs further processing\ninto a simpler form called _Pickles_.\n\nThe compiler compiles the AST produced by the parser into pickles:\n\n```mermaid\ngraph LR\n    A[AST] --\u003e|Compiler| B[Pickles]\n```\n\nThe rationale is to decouple Gherkin from Cucumber so that Cucumber is open to\nsupport alternative formats to Gherkin (for example Markdown).\n\nThe simpler _Pickles_ data structure also simplifies the internals of Cucumber.\nWith the compilation logic maintained in the Gherkin library\nwe can easily use the same test suite for all implementations to verify that\ncompilation is behaving consistently between implementations.\n\nEach `Scenario` will be compiled into a `Pickle`. A `Pickle` has a list of\n`PickleStep`, derived from the steps in a `Scenario`.\n\nEach `Examples` row under `Scenario Outline` will also be compiled into a `Pickle`.\n\nAny `Background` steps will also be compiled into a `Pickle`.\n\nEvery tag, like `@a`, will be compiled into a `Pickle` as well (inheriting tags from parent elements\nin the Gherkin AST).\n\nExample:\n\n```gherkin\n@a\nFeature:\n  @b @c\n  Scenario Outline:\n    Given \u003cx\u003e\n\n    Examples:\n      | x |\n      | y |\n\n  @d @e\n  Scenario Outline:\n    Given \u003cm\u003e\n\n    @f\n    Examples:\n      | m |\n      | n |\n```\n\nUsing the [CLI](#cli) we can compile this into several pickle objects:\n\n```console\ngherkin testdata/good/readme_example.feature --no-source --no-ast | jq\n```\n\nOutput:\n\n```json\n{\n  \"type\": \"pickle\",\n  \"uri\": \"testdata/good/readme_example.feature\",\n  \"pickle\": {\n    \"name\": \"\",\n    \"steps\": [\n      {\n        \"text\": \"y\",\n        \"arguments\": [],\n        \"locations\": [\n          {\n            \"line\": 9,\n            \"column\": 7\n          },\n          {\n            \"line\": 5,\n            \"column\": 11\n          }\n        ]\n      }\n    ],\n    \"tags\": [\n      {\n        \"name\": \"@a\",\n        \"location\": {\n          \"line\": 1,\n          \"column\": 1\n        }\n      },\n      {\n        \"name\": \"@b\",\n        \"location\": {\n          \"line\": 3,\n          \"column\": 3\n        }\n      },\n      {\n        \"name\": \"@c\",\n        \"location\": {\n          \"line\": 3,\n          \"column\": 6\n        }\n      }\n    ],\n    \"locations\": [\n      {\n        \"line\": 9,\n        \"column\": 7\n      },\n      {\n        \"line\": 4,\n        \"column\": 3\n      }\n    ]\n  }\n}\n{\n  \"type\": \"pickle\",\n  \"uri\": \"testdata/good/readme_example.feature\",\n  \"pickle\": {\n    \"name\": \"\",\n    \"steps\": [\n      {\n        \"text\": \"n\",\n        \"arguments\": [],\n        \"locations\": [\n          {\n            \"line\": 18,\n            \"column\": 7\n          },\n          {\n            \"line\": 13,\n            \"column\": 11\n          }\n        ]\n      }\n    ],\n    \"tags\": [\n      {\n        \"name\": \"@a\",\n        \"location\": {\n          \"line\": 1,\n          \"column\": 1\n        }\n      },\n      {\n        \"name\": \"@d\",\n        \"location\": {\n          \"line\": 11,\n          \"column\": 3\n        }\n      },\n      {\n        \"name\": \"@e\",\n        \"location\": {\n          \"line\": 11,\n          \"column\": 6\n        }\n      },\n      {\n        \"name\": \"@f\",\n        \"location\": {\n          \"line\": 15,\n          \"column\": 5\n        }\n      }\n    ],\n    \"locations\": [\n      {\n        \"line\": 18,\n        \"column\": 7\n      },\n      {\n        \"line\": 12,\n        \"column\": 3\n      }\n    ]\n  }\n}\n```\n\nEach `Pickle` event also contains the path to the original source. This is useful for\ngenerating reports and stack traces when a Scenario fails.\n\nCucumber will further transform this list of `Pickle` objects to a list of `TestCase`\nobjects. `TestCase` objects link to user code such as Hooks and Step Definitions.\n\n## Building Gherkin\n\nSee [`CONTRIBUTING.md`](CONTRIBUTING.md)\n\n## Markdown with Gherkin\n\nSee [Markdown with Gherkin](./MARKDOWN_WITH_GHERKIN.md).\n\n## Projects using Gherkin\n\n- [cucumber-jvm](https://github.com/cucumber/cucumber-jvm)\n- [cucumber-ruby](https://github.com/cucumber/cucumber-ruby)\n- [cucumber-js](https://github.com/cucumber/cucumber-js)\n- [godog](https://github.com/cucumber/godog)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcucumber%2Fgherkin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcucumber%2Fgherkin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcucumber%2Fgherkin/lists"}