Gherkin parser and compiler for Java providing complete parsing of Gherkin feature files into AST and executable Pickles for BDD testing frameworks
—
The GherkinParser provides the main entry point for parsing Gherkin feature files into structured data formats. It supports multiple input sources and configurable output options through a builder pattern.
Create and configure a GherkinParser instance using the builder pattern.
/**
* Create a new GherkinParser builder
* @return Builder instance for configuration
*/
public static GherkinParser.Builder builder();
/**
* Builder for configuring GherkinParser options
*/
public static final class Builder {
/**
* Configure whether to include source information in output
* @param includeSource true to include source data (default: true)
* @return this builder for chaining
*/
public Builder includeSource(boolean includeSource);
/**
* Configure whether to include GherkinDocument AST in output
* @param includeGherkinDocument true to include AST (default: true)
* @return this builder for chaining
*/
public Builder includeGherkinDocument(boolean includeGherkinDocument);
/**
* Configure whether to include compiled Pickles in output
* @param includePickles true to include pickles (default: true)
* @return this builder for chaining
*/
public Builder includePickles(boolean includePickles);
/**
* Set custom ID generator for element identification
* @param idGenerator custom ID generator implementation
* @return this builder for chaining
*/
public Builder idGenerator(IdGenerator idGenerator);
/**
* Build the configured GherkinParser instance
* @return configured parser ready for use
*/
public GherkinParser build();
}Parse Gherkin content from various sources including files, streams, and byte arrays.
/**
* Parse a Gherkin feature file from filesystem path
* @param source Path to the .feature file
* @return Stream of Envelope messages containing parse results
* @throws IOException if file cannot be read
*/
public Stream<Envelope> parse(Path source) throws IOException;
/**
* Parse Gherkin content from InputStream
* @param uri URI identifier for the source (used in messages)
* @param source InputStream containing Gherkin content
* @return Stream of Envelope messages containing parse results
* @throws IOException if stream cannot be read
*/
public Stream<Envelope> parse(String uri, InputStream source) throws IOException;
/**
* Parse Gherkin content from byte array
* @param uri URI identifier for the source (used in messages)
* @param source byte array containing Gherkin content
* @return Stream of Envelope messages containing parse results
*/
public Stream<Envelope> parse(String uri, byte[] source);
/**
* Parse Gherkin content from existing Envelope message
* @param envelope Envelope containing Source message with Gherkin content
* @return Stream of Envelope messages containing parse results
*/
public Stream<Envelope> parse(Envelope envelope);Usage Examples:
// Parse from file path
GherkinParser parser = GherkinParser.builder().build();
Stream<Envelope> messages = parser.parse(Paths.get("features/login.feature"));
// Parse from string content
String gherkinContent = """
Feature: User Login
Scenario: Valid credentials
Given a user exists
When they enter valid credentials
Then they should be logged in
""";
Stream<Envelope> messages = parser.parse("test.feature",
gherkinContent.getBytes(StandardCharsets.UTF_8));
// Configure parser options
GherkinParser customParser = GherkinParser.builder()
.includeSource(false) // Skip source in output
.includePickles(true) // Include executable pickles
.idGenerator(() -> "custom-id") // Use custom ID generation
.build();Process the stream of Envelope messages returned by parsing operations.
// Core message types returned in Envelope stream
interface Envelope {
Optional<Source> getSource(); // Original source content
Optional<GherkinDocument> getGherkinDocument(); // Parsed AST
Optional<Pickle> getPickle(); // Executable scenario
Optional<ParseError> getParseError(); // Parse error details
}
interface GherkinDocument {
String getUri();
Optional<Feature> getFeature();
List<Comment> getComments();
}
interface Pickle {
String getId();
String getUri();
String getName();
String getLanguage();
List<PickleStep> getSteps();
List<PickleTag> getTags();
List<String> getAstNodeIds();
}Message Processing Examples:
// Process all message types
parser.parse(featureFile).forEach(envelope -> {
// Handle source information
envelope.getSource().ifPresent(source ->
System.out.println("Parsing: " + source.getUri()));
// Handle parsed AST
envelope.getGherkinDocument().ifPresent(doc -> {
doc.getFeature().ifPresent(feature ->
System.out.println("Feature: " + feature.getName()));
});
// Handle executable scenarios
envelope.getPickle().ifPresent(pickle -> {
System.out.println("Scenario: " + pickle.getName());
pickle.getSteps().forEach(step ->
System.out.println(" " + step.getText()));
});
// Handle parse errors
envelope.getParseError().ifPresent(error ->
System.err.println("Parse error: " + error.getMessage()));
});
// Collect only pickles for test execution
List<Pickle> scenarios = parser.parse(featureFile)
.map(Envelope::getPickle)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
// Extract feature metadata
Optional<Feature> feature = parser.parse(featureFile)
.map(Envelope::getGherkinDocument)
.filter(Optional::isPresent)
.map(Optional::get)
.map(GherkinDocument::getFeature)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();Handle parsing errors and exceptions during Gherkin processing.
// Exception types thrown by the parser (package-private but visible to users)
ParserException.NoSuchLanguageException // Unsupported language code
ParserException.UnexpectedTokenException // Syntax errors in Gherkin
ParserException.UnexpectedEOFException // Premature end of file
ParserException.CompositeParserException // Multiple parsing errors
ParserException.AstBuilderException // AST construction failures
GherkinException // General processing errorsError Handling Examples:
import io.cucumber.gherkin.ParserException;
import io.cucumber.gherkin.GherkinException;
try {
Stream<Envelope> messages = parser.parse(featureFile);
// Check for parse errors in the stream
List<ParseError> errors = messages
.map(Envelope::getParseError)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
if (!errors.isEmpty()) {
errors.forEach(error ->
System.err.println("Parse error at " +
error.getSource().getLocation().getLine() + ": " +
error.getMessage()));
}
} catch (ParserException.NoSuchLanguageException e) {
System.err.println("Unsupported language in feature file: " + e.getMessage());
} catch (ParserException.UnexpectedTokenException e) {
System.err.println("Syntax error in Gherkin at line " +
e.location.getLine() + ": " + e.getMessage());
System.err.println("Expected: " + String.join(", ", e.expectedTokenTypes));
} catch (ParserException.CompositeParserException e) {
System.err.println("Multiple parsing errors found:");
e.errors.forEach(error ->
System.err.println(" - " + error.getMessage()));
} catch (GherkinException e) {
System.err.println("General parsing error: " + e.getMessage());
if (e.getCause() != null) {
System.err.println("Caused by: " + e.getCause().getMessage());
}
} catch (IOException e) {
System.err.println("Failed to read feature file: " + e.getMessage());
}Language Error Example:
// This will throw NoSuchLanguageException
String invalidLanguageFeature = """
# language: xyz-invalid
Feature: Test
Scenario: Example
Given something
""";
try {
parser.parse("test.feature", invalidLanguageFeature.getBytes());
} catch (ParserException.NoSuchLanguageException e) {
System.err.println("Language 'xyz-invalid' is not supported");
// Show available languages
GherkinDialectProvider provider = new GherkinDialectProvider();
System.out.println("Available languages: " + provider.getLanguages());
}Install with Tessl CLI
npx tessl i tessl/maven-io-cucumber--gherkin