gherkin

    A Gherkin parser for MoonBit with DOM, visitor, fold, and SAX-style APIs

    gherkin
    bdd
    cucumber
    parser
    testing
    Download zip
    Author
    Version
    0.3.0
    License
    Apache-2.0
    Last updated
    7 months ago
    Downloads
    1K

    Dependencies

    #moonrockz/gherkin

    A Gherkin parser for MoonBit. Parses .feature files used in Behavior-Driven Development (BDD) with Cucumber and similar frameworks.

    #Installation

    moon add moonrockz/gherkin

    #Quick Start

    let source = @gherkin.Source::from_string(
    "Feature: Login\n Scenario: Success\n Given a user\n When they log in\n Then they see the dashboard",
    )
    let doc = @gherkin.parse!(source)
    let feature = doc.feature.unwrap()
    // feature.name == "Login"

    #Four Parsing APIs

    #DOM-Based

    Parse to a full AST for random access to the document tree.

    let doc = @gherkin.parse!(source)
    // doc.feature, doc.comments — full tree

    #Visitor Pattern

    Traverse the AST depth-first, overriding only the node types you care about.

    doc.accept(my_visitor)

    #Functional Fold

    Thread an accumulator through the tree with flow control (Continue, SkipChildren, Stop).

    let count = doc.fold(0, {
    ..@gherkin.GherkinFold::default(),
    visit_step: @gherkin.continuing(fn(n, _) { n + 1 }),
    })

    #SAX-Style Handler

    Push-based event-driven parsing without building an AST.

    @gherkin.parse_with_handler!(source, my_handler)

    #WASM Component

    The parser is available as a WASM Component for use from any language with a Component Model runtime.

    mise run build:component # produces _build/gherkin.component.wasm

    Exports three interfaces (parse, tokenize, write) — see examples/ for Python and JavaScript usage.

    #CLI

    Parse a .feature file to JSON:

    moon run src/cmd/main -- path/to/file.feature

    Parse from stdin:

    echo "Feature: Test" | moon run src/cmd/main -- -

    #License

    Apache-2.0

    #moonrockz/gherkin

    A Gherkin parser providing four APIs: DOM tree, visitor, functional fold, and SAX-style handler.

    #Source

    All parsing starts with a Source, an opaque wrapper around the input text.

    ///|
    test "create a source from a string" {
    let src = Source::from_string(
    "Feature: Hello\n Scenario: World",
    uri="test.feature",
    )
    inspect(src.uri(), content="Some(\"test.feature\")")
    inspect(src.line_count(), content="2")
    inspect(src.line(1), content="Some(\"Feature: Hello\")")
    }

    #DOM Parsing

    parse returns a GherkinDocument containing the full AST.

    ///|
    test "parse a feature with scenarios and steps" {
    let input =
    #|Feature: Calculator
    #| Scenario: Addition
    #| Given two numbers
    #| When I add them
    #| Then I get the sum
    let doc = parse(Source::from_string(input))
    let feature = doc.feature.unwrap()
    inspect(feature.name, content="Calculator")
    inspect(feature.language, content="en")
    guard feature.children[0] is Scenario(s)
    inspect(s.name, content="Addition")
    inspect(s.steps.length(), content="3")
    inspect(s.steps[0].keyword_type, content="Context")
    inspect(s.steps[1].keyword_type, content="Action")
    inspect(s.steps[2].keyword_type, content="Outcome")
    }

    #Data Tables

    Steps can have a DataTable argument with rows and cells.

    ///|
    test "parse a step with a data table" {
    let input =
    #|Feature: Users
    #| Scenario: List users
    #| Given users:
    #| | name | age |
    #| | Alice | 30 |
    #| | Bob | 25 |
    let doc = parse(Source::from_string(input))
    guard doc.feature.unwrap().children[0] is Scenario(s)
    guard s.steps[0].argument is Some(DataTable(table))
    inspect(table.rows.length(), content="3")
    inspect(table.rows[0].cells[0].value, content="name")
    inspect(table.rows[1].cells[0].value, content="Alice")
    inspect(table.rows[2].cells[1].value, content="25")
    }

    #Doc Strings

    Steps can have a DocString argument with an optional media type.

    ///|
    test "parse a step with a doc string" {
    let input =
    #|Feature: Payloads
    #| Scenario: JSON body
    #| Given a request body:
    #| ```json
    #| {"key": "value"}
    #| ```
    let doc = parse(Source::from_string(input))
    guard doc.feature.unwrap().children[0] is Scenario(s)
    guard s.steps[0].argument is Some(DocString(ds))
    inspect(ds.media_type, content="Some(\"json\")")
    assert_true(ds.content.contains("key"))
    }

    #Tags

    Tags decorate features, scenarios, and examples.

    ///|
    test "parse tags on features and scenarios" {
    let input =
    #|@smoke @regression
    #|Feature: Tagged
    #| @critical
    #| Scenario: Important
    #| Given a step
    let doc = parse(Source::from_string(input))
    let feature = doc.feature.unwrap()
    inspect(feature.tags.length(), content="2")
    inspect(feature.tags[0].name, content="@smoke")
    guard feature.children[0] is Scenario(s)
    inspect(s.tags[0].name, content="@critical")
    }

    #JSON Serialization

    All AST types implement ToJson.

    ///|
    test "serialize a document to JSON" {
    let doc = parse(
    Source::from_string("Feature: JSON\n Scenario: Test\n Given a step"),
    )
    let text = doc.to_json().stringify()
    assert_true(text.contains("JSON"))
    assert_true(text.contains("Test"))
    }

    #Visitor Pattern

    Implement GherkinVisitor and override only the methods you need. All methods have default no-op implementations.

    ///|
    struct ScenarioCounter {
    mut count : Int
    }

    ///|
    impl GherkinVisitor for ScenarioCounter with visit_scenario(self, _scenario) {
    self.count 1
    }

    ///|
    test "count scenarios with a visitor" {
    let input =
    #|Feature: Counter
    #| Scenario: First
    #| Given a
    #| Scenario: Second
    #| Given b
    let doc = parse(Source::from_string(input))
    let counter : ScenarioCounter = { count: 0 }
    doc.accept(counter)
    inspect(counter.count, content="2")
    }

    #Functional Fold

    GherkinFold threads an accumulator through the AST. Use GherkinFold::default() for a passthrough fold, then override fields. The continuing helper lifts plain functions into fold callbacks.

    ///|
    test "count steps with fold" {
    let input =
    #|Feature: Fold
    #| Scenario: One
    #| Given step 1
    #| When step 2
    #| Scenario: Two
    #| Then step 3
    let doc = parse(Source::from_string(input))
    let step_count = doc.fold(0, {
    ..GherkinFold::default(),
    visit_step: continuing(fn(n, _step) { n + 1 }),
    })
    inspect(step_count, content="3")
    }

    #Flow Control

    FoldAction controls traversal: Continue descends into children, SkipChildren skips children but continues siblings, Stop halts immediately.

    ///|
    let step_count = doc.fold(0, {
    ..GherkinFold::default(),
    visit_scenario: fn(n, scenario) {
    if scenario.tags.iter().any(fn(t) { t.name == "@skip" }) {
    SkipChildren(n) // skip steps inside @skip scenarios
    } else {
    Continue(n)
    }
    },
    visit_step: continuing(fn(n, _) { n + 1 }),
    })

    #SAX-Style Handler

    GherkinHandler receives push-based events as the parser encounters elements. No AST is built — useful for streaming or memory-constrained scenarios.

    ///|
    struct EventLog {
    events : Array[String]
    }

    ///|
    impl GherkinHandler for EventLog with on_feature(self, event) {
    self.events.push("feature:\{event.name}")
    }

    ///|
    impl GherkinHandler for EventLog with on_scenario(self, event) {
    self.events.push("scenario:\{event.name}")
    }

    ///|
    impl GherkinHandler for EventLog with on_step(self, event) {
    self.events.push("step:\{event.text}")
    }

    ///|
    test "log events with a handler" {
    let input =
    #|Feature: Events
    #| Scenario: Example
    #| Given a step
    #| When an action
    let logger : EventLog = { events: [] }
    parse_with_handler(Source::from_string(input), logger)
    inspect(logger.events[0], content="feature:Events")
    inspect(logger.events[1], content="scenario:Example")
    inspect(logger.events[2], content="step:a step")
    inspect(logger.events[3], content="step:an action")
    }

    #Lexer

    The lower-level lexer API is useful for syntax highlighting or custom parsing.

    tokenize converts a full source to tokens eagerly. Lexer provides a lazy iterator.

    ///|
    test "tokenize a source" {
    let tokens = tokenize(Source::from_string("Feature: Test\n Given a step"))
    guard tokens[0] is FeatureLine(_, kw, name)
    inspect(kw, content="Feature")
    inspect(name, content="Test")
    guard tokens[1] is StepLine(_, _, kt, text)
    inspect(kt, content="Context")
    inspect(text, content="a step")
    }

    ///|
    test "lazy iteration with Lexer" {
    let lexer = Lexer::new(Source::from_string("Given a\nWhen b\nThen c"))
    let mut count = 0
    for tok in lexer.iter() {
    match tok {
    StepLine(_, _, _, _) => count 1
    _ => ()
    }
    }
    inspect(count, content="3")
    }

    #Error Handling

    Parse errors carry a message and location.

    ///|
    test "handle parse errors" {
    let input =
    #|Feature: Tables
    #| Scenario: Bad
    #| Given a table:
    #| | a | b |
    #| | 1 | 2 | 3 |
    let result = try {
    let _ = parse(Source::from_string(input))
    "ok"
    } catch {
    InconsistentTableCells(message~, ..) => message
    _ => "other"
    }
    assert_true(result.contains("inconsistent cell count"))
    }

    #i18n

    Use # language: xx on the first line to parse non-English Gherkin.

    ///|
    test "parse French Gherkin" {
    let input =
    #|# language: fr
    #|Fonctionnalité: Connexion
    #| Scénario: Succès
    #| Soit un utilisateur
    let doc = parse(Source::from_string(input))
    let feature = doc.feature.unwrap()
    inspect(feature.language, content="fr")
    inspect(feature.keyword, content="Fonctionnalit\u00e9")
    }

    GherkinHandler

    pub(open) trait GherkinHandler {
    on_document(Self) -> Unit = _
    on_end_document(Self) -> Unit = _
    on_feature(Self, FeatureEvent) -> Unit = _
    on_end_feature(Self) -> Unit = _
    on_rule(Self, RuleEvent) -> Unit = _
    on_end_rule(Self) -> Unit = _
    on_background(Self, BackgroundEvent) -> Unit = _
    on_end_background(Self) -> Unit = _
    on_scenario(Self, ScenarioEvent) -> Unit = _
    on_end_scenario(Self) -> Unit = _
    on_step(Self, StepEvent) -> Unit = _
    on_examples(Self, ExamplesEvent) -> Unit = _
    on_tag(Self, TagEvent) -> Unit = _
    on_comment(Self, CommentEvent) -> Unit = _
    on_doc_string(Self, DocStringEvent) -> Unit = _
    on_data_table(Self, DataTableEvent) -> Unit = _
    }

    A handler for push-based (SAX-style) parsing events.

    All methods have default no-op implementations. Override only the methods for events you want to handle.

    The parser drives the process, calling handler methods as it encounters structural elements.

    GherkinVisitor

    pub(open) trait GherkinVisitor {
    visit_document(Self, GherkinDocument) -> Unit = _
    visit_feature(Self, Feature) -> Unit = _
    visit_rule(Self, Rule) -> Unit = _
    visit_background(Self, Background) -> Unit = _
    visit_scenario(Self, Scenario) -> Unit = _
    visit_step(Self, Step) -> Unit = _
    visit_doc_string(Self, DocString) -> Unit = _
    visit_data_table(Self, DataTable) -> Unit = _
    visit_examples(Self, Examples) -> Unit = _
    visit_tag(Self, Tag) -> Unit = _
    visit_comment(Self, Comment) -> Unit = _
    visit_table_row(Self, TableRow) -> Unit = _
    }

    A visitor for traversing a GherkinDocument AST.

    All methods have default no-op implementations. Override only the methods for node types you want to process.

    The data structure controls traversal order (depth-first, document order). Use GherkinDocument::accept to begin traversal.

    ParseError

    pub suberror ParseError {
    UnexpectedToken(String, Location)
    UnexpectedEof(String, Location)
    InconsistentTableCells(String, Location)
    CompositeError(Array[ParseError])
    }

    Errors that can occur during Gherkin parsing.

    Background

    pub(all) struct Background {
    location : Location
    keyword : String
    name : String
    description : String
    id : String
    steps : Array[Step]
    }

    A background section providing shared setup steps.
    impl Eq for Background
    impl Show for Background

    Background::accept

    fn Background::accept(self : Background, visitor : &GherkinVisitor) -> Unit

    BackgroundEvent

    pub(all) struct BackgroundEvent {
    location : Location
    keyword : String
    name : String
    description : String
    }

    A background start event. Steps arrive as subsequent events.

    Comment

    pub(all) struct Comment {
    location : Location
    text : String
    }

    A comment line in the source.
    impl Eq for Comment
    impl Show for Comment
    impl ToJson for Comment

    Comment::accept

    fn Comment::accept(self : Comment, visitor : &GherkinVisitor) -> Unit

    CommentEvent

    pub(all) struct CommentEvent {
    location : Location
    text : String
    }

    A comment event (leaf).
    impl Eq for CommentEvent

    DataTable

    pub(all) struct DataTable {
    location : Location
    rows : Array[TableRow]
    }

    A data table step argument.
    impl Eq for DataTable
    impl Show for DataTable
    impl ToJson for DataTable

    DataTable::accept

    fn DataTable::accept(self : DataTable, visitor : &GherkinVisitor) -> Unit

    DataTableEvent

    pub(all) struct DataTableEvent {
    location : Location
    rows : Array[TableRow]
    }

    A data table event (self-contained with all rows).

    DocString

    pub(all) struct DocString {
    location : Location
    media_type : String?
    content : String
    delimiter : String
    }

    A doc string step argument.
    impl Eq for DocString
    impl Show for DocString
    impl ToJson for DocString

    DocString::accept

    fn DocString::accept(self : DocString, visitor : &GherkinVisitor) -> Unit

    DocStringEvent

    pub(all) struct DocStringEvent {
    location : Location
    media_type : String?
    content : String
    delimiter : String
    }

    A doc string event (leaf).

    Examples

    pub(all) struct Examples {
    location : Location
    tags : Array[Tag]
    keyword : String
    name : String
    description : String
    id : String
    table_header : TableRow?
    table_body : Array[TableRow]
    }

    An examples table attached to a scenario outline.
    impl Eq for Examples
    impl Show for Examples
    impl ToJson for Examples

    Examples::accept

    fn Examples::accept(self : Examples, visitor : &GherkinVisitor) -> Unit

    ExamplesEvent

    pub(all) struct ExamplesEvent {
    location : Location
    tags : Array[Tag]
    keyword : String
    name : String
    description : String
    table_header : TableRow?
    table_body : Array[TableRow]
    }

    An examples table event (self-contained with header + body).
    impl Eq for ExamplesEvent

    Feature

    pub(all) struct Feature {
    location : Location
    tags : Array[Tag]
    language : String
    keyword : String
    name : String
    description : String
    children : Array[FeatureChild]
    }

    A feature, the top-level structural element in a Gherkin document.
    impl Eq for Feature
    impl Show for Feature
    impl ToJson for Feature

    Feature::accept

    fn Feature::accept(self : Feature, visitor : &GherkinVisitor) -> Unit

    FeatureChild

    pub(all) enum FeatureChild {
    Background(Background)
    Scenario(Scenario)
    Rule(Rule)
    }

    A child element of a Feature: Background, Scenario, or Rule.
    impl Eq for FeatureChild

    FeatureEvent

    pub(all) struct FeatureEvent {
    location : Location
    tags : Array[Tag]
    language : String
    keyword : String
    name : String
    description : String
    }

    A feature start event. Children arrive as subsequent events.
    impl Eq for FeatureEvent

    FoldAction

    pub enum FoldAction[A] {
    Continue(A)
    SkipChildren(A)
    Stop(A)
    }

    Control flow for fold traversal.

    • Continue(a) — proceed into children with updated state
    • SkipChildren(a) — skip children, continue with siblings
    • Stop(a) — halt traversal immediately, return final state
    impl Eq for FoldAction[A]
    impl Show for FoldAction[A]

    FoldAction::is_stop

    fn[A] FoldAction::is_stop(self : FoldAction[A]) -> Bool

    Returns true if this action is Stop.

    FoldAction::value

    fn[A] FoldAction::value(self : FoldAction[A]) -> A

    Extract the accumulated value from a FoldAction.

    GherkinDocument

    pub(all) struct GherkinDocument {
    source : Source
    feature : Feature?
    comments : Array[Comment]
    }

    The root of a parsed Gherkin document.

    GherkinDocument::accept

    fn GherkinDocument::accept(self : GherkinDocument, visitor : &GherkinVisitor) -> Unit

    Accept a visitor on a GherkinDocument, traversing the entire tree in depth-first document order.

    GherkinDocument::empty

    Create an empty GherkinDocument with no feature and no comments.

    GherkinDocument::fold

    fn[A] GherkinDocument::fold(self : GherkinDocument, init : A, folder : GherkinFold[A]) -> A

    Fold over a GherkinDocument AST, threading an accumulator through each node in depth-first document order.

    Respects FoldAction flow control:
    • Continue descends into children
    • SkipChildren skips children, continues with siblings
    • Stop halts traversal and returns immediately

    GherkinEvent

    pub(all) enum GherkinEvent {
    DocumentStart
    DocumentEnd
    FeatureStart(FeatureEvent)
    FeatureEnd
    RuleStart(RuleEvent)
    RuleEnd
    BackgroundStart(BackgroundEvent)
    BackgroundEnd
    ScenarioStart(ScenarioEvent)
    ScenarioEnd
    Step(StepEvent)
    DocString(DocStringEvent)
    DataTable(DataTableEvent)
    Examples(ExamplesEvent)
    Tag(TagEvent)
    Comment(CommentEvent)
    }

    Events emitted during pull-based parsing. Follows nested lifecycle: Document -> Feature -> (Background|Scenario|Rule) -> Steps -> End*
    impl Eq for GherkinEvent

    GherkinFold

    pub(all) struct GherkinFold[A] {
    visit_document : (A, GherkinDocument) -> FoldAction[A]
    visit_feature : (A, Feature) -> FoldAction[A]
    visit_rule : (A, Rule) -> FoldAction[A]
    visit_background : (A, Background) -> FoldAction[A]
    visit_scenario : (A, Scenario) -> FoldAction[A]
    visit_step : (A, Step) -> FoldAction[A]
    visit_doc_string : (A, DocString) -> FoldAction[A]
    visit_data_table : (A, DataTable) -> FoldAction[A]
    visit_examples : (A, Examples) -> FoldAction[A]
    visit_tag : (A, Tag) -> FoldAction[A]
    visit_comment : (A, Comment) -> FoldAction[A]
    visit_table_row : (A, TableRow) -> FoldAction[A]
    }

    A fold visitor record for functional accumulation over a Gherkin AST.

    Each field is a callback (A, Node) -> FoldAction[A] that receives the current accumulator and a node, and returns the next accumulator wrapped in a flow-control directive.

    Use GherkinFold::default() to get a fold where every callback passes state through unchanged, then override the ones you need via struct update syntax:

    let count = doc.fold(0, { ..GherkinFold::default(), visit_scenario: fn(n, _) { Continue(n + 1) } })

    GherkinFold::default

    fn[A] GherkinFold::default() -> GherkinFold[A]

    Create a GherkinFold where every callback passes state through unchanged.

    GherkinReader

    pub struct GherkinReader {
    // private fields
    }

    A pull-based event reader for Gherkin documents.

    Produces a stream of GherkinEvent values that the consumer pulls one at a time. Supports peek(), next(), and iter().

    Two construction paths:
    • GherkinReader::new(source) — parses from raw source text
    • GherkinReader::from_document(doc) — walks an existing AST

    Events can be piped to a GherkinHandler via pipe().

    GherkinReader::from_document

    fn GherkinReader::from_document(doc : GherkinDocument) -> GherkinReader

    Create a GherkinReader from an already-parsed document.

    GherkinReader::iter

    Return an Iter[GherkinEvent] that lazily pulls events.

    GherkinReader::new

    fn GherkinReader::new(source : Source) -> GherkinReader raise ParseError

    Create a GherkinReader that parses the given source into events.

    GherkinReader::next

    Return the next event, advancing the reader position. Returns None when all events have been consumed.

    GherkinReader::peek

    Return the next event without advancing the reader position. Returns None when all events have been consumed.

    GherkinReader::pipe

    fn GherkinReader::pipe(self : GherkinReader, handler : &GherkinHandler) -> Unit

    Pipe all remaining events to a GherkinHandler.

    This is the pull-to-push adapter: pulls events from the reader and dispatches them to the handler's callback methods.

    GherkinWriter

    pub struct GherkinWriter {
    // private fields
    }

    A push-based Gherkin writer for building Gherkin text from arbitrary sources.

    Manages indentation and formatting automatically. Call structural methods (feature, scenario, etc.) with matching end_* methods, and leaf methods (step, tags, etc.) for content.

    Implements GherkinHandler, enabling reader-to-writer piping:

    let w = GherkinWriter::new() parse_with_handler(source, w) let output = w.to_string()

    GherkinWriter::background

    fn GherkinWriter::background(self : GherkinWriter, keyword : String, name? : String, description? : String) -> Unit

    Emit a background header. Call end_background() when done.

    GherkinWriter::comment

    fn GherkinWriter::comment(self : GherkinWriter, text : String) -> Unit

    Emit a comment line.

    GherkinWriter::data_table

    fn GherkinWriter::data_table(self : GherkinWriter, rows : Array[Array[String]]) -> Unit

    Emit a data table with column-aligned cells after a step.

    Each inner array is one row of cell values. All rows must have the same number of elements.

    GherkinWriter::doc_string

    fn GherkinWriter::doc_string(self : GherkinWriter, content : String, delimiter? : String, media_type? : String?) -> Unit

    Emit a doc string block after a step.

    GherkinWriter::end_background

    fn GherkinWriter::end_background(self : GherkinWriter) -> Unit

    Close the current background scope.

    GherkinWriter::end_feature

    fn GherkinWriter::end_feature(self : GherkinWriter) -> Unit

    Close the current feature scope.

    GherkinWriter::end_rule

    fn GherkinWriter::end_rule(self : GherkinWriter) -> Unit

    Close the current rule scope.

    GherkinWriter::end_scenario

    fn GherkinWriter::end_scenario(self : GherkinWriter) -> Unit

    Close the current scenario scope.

    GherkinWriter::examples

    fn GherkinWriter::examples(self : GherkinWriter, keyword : String, name? : String, description? : String, header? : Array[String], body? : Array[Array[String]]) -> Unit

    Emit an examples section with a pretty-printed table.

    header is the column names, body is the data rows. Both are arrays of cell value strings.

    GherkinWriter::feature

    fn GherkinWriter::feature(self : GherkinWriter, keyword : String, name : String, language? : String, description? : String) -> Unit

    Emit a feature header. Call end_feature() when done.

    When language is not "en", a # language: directive is emitted first.

    GherkinWriter::new

    Create a new GherkinWriter with the given configuration.

    GherkinWriter::rule

    fn GherkinWriter::rule(self : GherkinWriter, keyword : String, name : String, description? : String) -> Unit

    Emit a rule header. Call end_rule() when done.

    GherkinWriter::scenario

    fn GherkinWriter::scenario(self : GherkinWriter, keyword : String, name : String, description? : String) -> Unit

    Emit a scenario header. Call end_scenario() when done.

    GherkinWriter::step

    fn GherkinWriter::step(self : GherkinWriter, keyword : String, text : String) -> Unit

    Emit a step line.

    GherkinWriter::tags

    fn GherkinWriter::tags(self : GherkinWriter, names : Array[String]) -> Unit

    Emit tags on one line, space-separated.

    Call before the structural element the tags decorate. Tags are emitted at the current indent level.

    GherkinWriter::to_string

    fn GherkinWriter::to_string(self : GherkinWriter) -> String

    Return the accumulated Gherkin text.

    KeywordType

    pub(all) enum KeywordType {
    Context
    Action
    Outcome
    Conjunction
    Unknown
    }

    The type of a step keyword, classifying its semantic role.
    impl Eq for KeywordType
    impl Show for KeywordType

    Lexer

    pub struct Lexer {
    // private fields
    }

    impl Show for Lexer

    Lexer::iter

    fn Lexer::iter(self : Lexer) -> Iter[Token]

    Return an Iter[Token] that lazily produces tokens from the source.

    Lexer::new

    fn Lexer::new(source : Source) -> Lexer

    Lexer::next

    fn Lexer::next(self : Lexer) -> Token?

    Return the next token, advancing the lexer state.

    LexerState

    pub enum LexerState {
    Normal
    InDocString(String)
    }

    Internal state tracked between lines during tokenization. Gherkin is mostly stateless line-by-line except inside doc strings.
    impl Eq for LexerState
    impl Show for LexerState

    Location

    pub(all) struct Location {
    line : Int
    column : Int?
    }

    A location in the source text.
    impl Eq for Location
    impl Show for Location
    impl ToJson for Location

    Rule

    pub(all) struct Rule {
    location : Location
    tags : Array[Tag]
    keyword : String
    name : String
    description : String
    id : String
    children : Array[RuleChild]
    }

    A rule grouping related scenarios under a business rule.
    impl Eq for Rule
    impl Show for Rule
    impl ToJson for Rule

    Rule::accept

    fn Rule::accept(self : Rule, visitor : &GherkinVisitor) -> Unit

    RuleChild

    pub(all) enum RuleChild {
    Background(Background)
    Scenario(Scenario)
    }

    A child element of a Rule: either a Background or a Scenario.
    impl Eq for RuleChild
    impl Show for RuleChild
    impl ToJson for RuleChild

    RuleEvent

    pub(all) struct RuleEvent {
    location : Location
    tags : Array[Tag]
    keyword : String
    name : String
    description : String
    }

    A rule start event. Children arrive as subsequent events.
    impl Eq for RuleEvent
    impl Show for RuleEvent

    Scenario

    pub(all) struct Scenario {
    location : Location
    tags : Array[Tag]
    kind : ScenarioKind
    keyword : String
    name : String
    description : String
    id : String
    steps : Array[Step]
    examples : Array[Examples]
    }

    A scenario (or scenario outline) within a feature or rule.
    impl Eq for Scenario
    impl Show for Scenario
    impl ToJson for Scenario

    Scenario::accept

    fn Scenario::accept(self : Scenario, visitor : &GherkinVisitor) -> Unit

    ScenarioEvent

    pub(all) struct ScenarioEvent {
    location : Location
    tags : Array[Tag]
    kind : ScenarioKind
    keyword : String
    name : String
    description : String
    }

    A scenario start event. Steps/examples arrive as subsequent events.
    impl Eq for ScenarioEvent

    ScenarioKind

    pub(all) enum ScenarioKind {
    Scenario
    ScenarioOutline
    }

    Distinguishes a Scenario from a Scenario Outline.
    impl Eq for ScenarioKind

    Source

    pub struct Source {
    // private fields
    }

    A source of Gherkin text, bundling content with metadata.

    The internal representation is opaque — callers use factory functions to create and accessor methods to read. This allows the internal representation to evolve without breaking consumers.
    impl Eq for Source
    impl Show for Source
    impl ToJson for Source
    impl FromJson for Source

    Source::content

    fn Source::content(self : Source) -> String

    Returns the full content as a single string, with lines joined by \n.

    This allocates a new string. For line-by-line access, prefer line().

    Source::from_bytes

    fn Source::from_bytes(bytes : Bytes, uri? : String) -> Source

    Create a Source from UTF-8 encoded bytes.

    Decodes the bytes to a string, then splits into lines. Invalid UTF-8 sequences are replaced with the Unicode replacement character.

    Source::from_string

    fn Source::from_string(content : String, uri? : String) -> Source

    Create a Source from a string, splitting into lines.

    Handles both Unix (\n) and Windows (\r\n) line endings. The optional uri identifies where the content came from (file path, URL, etc.).

    Source::line

    fn Source::line(self : Source, n : Int) -> String?

    Returns the line at the given 1-based line number, matching Location.line.

    Returns None if the line number is out of range.

    Source::line_count

    fn Source::line_count(self : Source) -> Int

    Returns the number of lines in this source.

    Source::uri

    fn Source::uri(self : Source) -> String?

    Returns the URI identifying this source, if any.

    Step

    pub(all) struct Step {
    location : Location
    keyword : String
    keyword_type : KeywordType
    text : String
    id : String
    argument : StepArgument?
    }

    A single step within a scenario or background.
    impl Eq for Step
    impl Show for Step
    impl ToJson for Step

    Step::accept

    fn Step::accept(self : Step, visitor : &GherkinVisitor) -> Unit

    StepArgument

    pub(all) enum StepArgument {
    DocString(DocString)
    DataTable(DataTable)
    }

    A step argument is either a DocString or a DataTable.
    impl Eq for StepArgument

    StepEvent

    pub(all) struct StepEvent {
    location : Location
    keyword : String
    keyword_type : KeywordType
    text : String
    }

    A step event (leaf -- no children).
    impl Eq for StepEvent
    impl Show for StepEvent

    TableCell

    pub(all) struct TableCell {
    location : Location
    value : String
    }

    A single cell in a data table row.
    impl Eq for TableCell
    impl Show for TableCell
    impl ToJson for TableCell

    TableRow

    pub(all) struct TableRow {
    location : Location
    id : String
    cells : Array[TableCell]
    }

    A row in a data table or examples table.
    impl Eq for TableRow
    impl Show for TableRow
    impl ToJson for TableRow

    TableRow::accept

    fn TableRow::accept(self : TableRow, visitor : &GherkinVisitor) -> Unit

    Tag

    pub(all) struct Tag {
    location : Location
    name : String
    id : String
    }

    A tag as it appears in source. The name field includes the '@' prefix (e.g., "@smoke").
    impl Eq for Tag
    impl Show for Tag
    impl ToJson for Tag

    Tag::accept

    fn Tag::accept(self : Tag, visitor : &GherkinVisitor) -> Unit

    TagEvent

    pub(all) struct TagEvent {
    location : Location
    name : String
    }

    A tag event (leaf).
    impl Eq for TagEvent
    impl Show for TagEvent

    Token

    pub enum Token {
    FeatureLine(Location, String, String)
    RuleLine(Location, String, String)
    BackgroundLine(Location, String, String)
    ScenarioLine(Location, String, String, ScenarioKind)
    ExamplesLine(Location, String, String)
    StepLine(Location, String, KeywordType, String)
    DocStringSeparator(Location, String, String?)
    TableRow(Location, Array[String])
    TagLine(Location, Array[String])
    Comment(Location, String)
    Language(Location, String)
    Empty(Location)
    Other(Location, String)
    Eof(Location)
    }

    A token produced by the lexer, representing a classified source line. Each variant carries its Location and the relevant parsed fragments.
    impl Eq for Token
    impl Show for Token
    impl ToJson for Token

    WriteConfig

    pub(all) struct WriteConfig {
    indent : String
    table_cell_padding : Int
    trailing_newline : Bool
    include_comments : Bool
    }

    Configuration for formatting Gherkin output.

    Use WriteConfig::default() for standard formatting, then override specific fields via struct update syntax:

    let config = { ..WriteConfig::default(), indent: " " }
    impl Eq for WriteConfig
    impl Show for WriteConfig

    WriteConfig::default

    fn WriteConfig::default() -> WriteConfig

    Create a WriteConfig with standard Gherkin formatting defaults.

    classify_line

    fn classify_line(line : String, line_num : Int, state : LexerState, language? : String) -> (Token, LexerState)

    Classify a single source line into a Token, returning the updated LexerState.

    This is the core pure function of the lexer. It examines one line at a time, using the current state to handle doc string regions correctly. The language parameter selects the keyword table for matching Gherkin keywords (Feature, Scenario, Given, etc.). Defaults to English ("en").

    continuing

    fn[A, N] continuing(f : (A, N) -> A) -> ((A, N) -> FoldAction[A])

    Lift a plain (A, N) -> A function into a FoldAction-returning callback that always continues into children.

    This reduces boilerplate when you don't need flow control:

    // Without continuing: visit_scenario: fn(n, _) { Continue(n + 1) } // With continuing: visit_scenario: continuing(fn(n, _) { n + 1 })

    parse

    fn parse(source : Source) -> GherkinDocument raise ParseError

    Parse a Gherkin document from a Source.

    parse_with_handler

    fn parse_with_handler(source : Source, handler : &GherkinHandler) -> Unit raise ParseError

    Parse a Source, pushing events to a handler.

    tokenize

    fn tokenize(source : Source) -> Array[Token]

    write

    fn write(doc : GherkinDocument, config? : WriteConfig) -> String

    Write a GherkinDocument AST as formatted Gherkin text.

    Produces well-formatted output with column-aligned tables, properly indented steps, and preserved i18n keywords.

    This is the inverse of parse: calling write(parse(source)) produces valid Gherkin that, when re-parsed, yields an equivalent AST.