Skip to content

Grammar Reference

This page contains the complete tree-sitter grammar for Vanya. This is the authoritative syntax specification.

Tree-sitter Grammar

// tree-sitter-vanya grammar definition
// See docs/DESIGN.md for the full grammar specification
module.exports = grammar({
name: 'vanya',
externals: $ => [
$._indent,
$._dedent,
$._newline,
],
extras: $ => [/[ \t]/],
word: $ => $.identifier,
conflicts: $ => [
[$.action_tokens],
[$.statement_block],
[$.multi_scenario],
],
rules: {
source_file: $ => seq(
repeat($._newline),
$.file_header,
repeat($._newline),
$.script_header,
repeat($._newline),
choice(
$.multi_scenario,
$.statement_block
),
repeat($._newline)
),
multi_scenario: $ => seq(
optional($.setup_block),
repeat($._newline),
$.scenario_block,
repeat(seq(repeat($._newline), $.scenario_block))
),
setup_block: $ => seq(
'Setup', 'for', 'each', 'Scenario', ':',
$._indent,
$.statement_block,
$._dedent
),
scenario_block: $ => seq(
'Scenario', ':', $.string,
$._indent,
$.statement_block,
$._dedent
),
file_header: $ => seq(
'Vanya', 'test', 'scenario',
'(', $.domain, '/', $.version, ')'
),
domain: $ => /[a-zA-Z0-9.-]+/,
version: $ => /v\d+\.\d+(\.\d+)?/,
script_header: $ => seq('Script', 'title:', $.string),
statement_block: $ => seq(
$._statement,
repeat(seq($._newline, optional($._statement)))
),
_statement: $ => choice(
$.within_stmt,
$.define_stmt,
$.capture_stmt,
$.export_stmt,
$.action_stmt,
$.comment
),
within_stmt: $ => seq(
'Within', 'the', $.context_name,
$._modifiers,
$._indent,
$.statement_block,
$._dedent
),
context_name: $ => /[^(\n]+/,
define_stmt: $ => prec(2, seq(
'Define', $.alias_name, '(', $.selector_list, ')'
)),
export_stmt: $ => prec(2, seq('Export', $.identifier)),
capture_stmt: $ => prec(2, seq('Capture', $.string, 'as', $.identifier)),
action_stmt: $ => seq(
$.action_tokens,
optional($._modifiers),
optional($.action_block)
),
// action_tokens can include hyphenated names (for alias references), numbers, and operators
action_tokens: $ => repeat1(choice($.identifier, $.hyphenated_identifier, $.string, $.number, $.operator)),
number: $ => /\d+/,
// Operators for comparison patterns like "expect json x == y"
operator: $ => choice('==', '!=', '>=', '<=', '>', '<'),
// Hyphenated identifier like submit-button
hyphenated_identifier: $ => /[a-zA-Z][a-zA-Z0-9_]*(-[a-zA-Z][a-zA-Z0-9_]*)+/,
action_block: $ => seq(
$._indent,
$._action_block_item,
repeat(seq($._newline, optional($._action_block_item))),
$._dedent
),
_action_block_item: $ => choice(
$.capture_stmt,
$.action_stmt
),
_modifiers: $ => choice(
$.parens_modifiers,
$.with_modifiers
),
parens_modifiers: $ => seq('(', $.key_value_list, ')'),
with_modifiers: $ => seq('with', $.key_value_list),
key_value_list: $ => seq($.key_value, repeat(seq(',', $.key_value))),
key_value: $ => seq($.identifier, ':', $._value),
_value: $ => choice($.string, $.unquoted_value),
selector_list: $ => seq($.key_value, repeat(seq(',', $.key_value))),
alias_name: $ => /[a-zA-Z][a-zA-Z0-9_-]*/,
identifier: $ => /[a-zA-Z][a-zA-Z0-9_]*/,
unquoted_value: $ => /[a-zA-Z][a-zA-Z0-9_-]*/,
string: $ => choice(
seq("'", /[^']*/, "'"),
seq('"', /[^"]*/, '"')
),
comment: $ => seq('#', /.*/),
}
});

Quick Reference

Top-Level Rules

RuleDescription
source_fileEntry point: file_header + script_header + (multi_scenario or statement_block)
file_headerVanya test scenario (<domain>/<version>)
script_headerScript title: <string>
statement_blockOne or more statements separated by newlines

Multi-Scenario Rules

RuleDescription
multi_scenarioOptional setup_block + one or more scenario_block
setup_blockSetup for each Scenario: + indented statement_block
scenario_blockScenario: <string> + indented statement_block

Statement Rules

RuleDescription
_statementChoice of: within_stmt, define_stmt, capture_stmt, export_stmt, action_stmt, comment
within_stmtWithin the <context_name> <modifiers> <indent> <statement_block> <dedent>
define_stmtDefine <alias_name> (<selector_list>)
capture_stmtCapture <string> as <identifier>
export_stmtExport <identifier>
action_stmt<action_tokens> + optional <modifiers> + optional <action_block>
comment# <text>

Action Rules

RuleDescription
action_tokensOne or more: identifier, hyphenated_identifier, string, number, or operator
action_blockIndented block containing capture_stmt or action_stmt items
operatorComparison operators: ==, !=, >=, <=, >, <

Modifier Rules

RuleDescription
_modifiersChoice of: parens_modifiers or with_modifiers
parens_modifiers(<key_value_list>)
with_modifierswith <key_value_list>
key_value_listComma-separated <key_value> pairs
key_value<identifier>: <value>
selector_listComma-separated <key_value> pairs (used in define_stmt)

Identifier and Literal Rules

RuleDescription
identifier[a-zA-Z][a-zA-Z0-9_]*
alias_name[a-zA-Z][a-zA-Z0-9_-]* (allows hyphens)
hyphenated_identifier[a-zA-Z][a-zA-Z0-9_]*(-[a-zA-Z][a-zA-Z0-9_]*)+ (e.g., submit-button)
context_name[^(\n]+ (any text until ( or newline)
domain[a-zA-Z0-9.-]+
versionv\d+\.\d+(\.\d+)? (e.g., v1.0, v2.1.3)
stringSingle or double quoted: '...' or "..."
number\d+
unquoted_value[a-zA-Z][a-zA-Z0-9_-]*

Keywords

All keywords are case-sensitive and fall into two categories:

Capitalized keywords (statement/structure beginnings):

  • Vanya, Script, Within, Define, Capture, Export, Setup, Scenario

Lowercase connectors:

  • test, scenario (file header: Vanya test scenario (...))
  • title: (script header: Script title: "...")
  • the (within block: Within the <context>)
  • as (capture statement: Capture "..." as <name>)
  • with (modifier syntax: <action> with <key>: <value>)

Indentation

Vanya uses significant indentation (like Python). The external scanner handles _indent, _dedent, and _newline tokens.