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], ],
rules: { source_file: $ => seq( repeat($._newline), $.file_header, repeat($._newline), $.script_header, repeat($._newline), $.statement_block, repeat($._newline) ),
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) and numbers action_tokens: $ => repeat1(choice($.identifier, $.hyphenated_identifier, $.string, $.number)),
number: $ => /\d+/,
// 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
| Rule | Description |
|---|---|
source_file | Entry point: file_header + script_header + statement_block |
file_header | Vanya test scenario (<domain>/<version>) |
script_header | Script title: <string> |
statement_block | One or more statements separated by newlines |
Statement Rules
| Rule | Description |
|---|---|
_statement | Choice of: within_stmt, define_stmt, capture_stmt, export_stmt, action_stmt, comment |
within_stmt | Within the <context_name> <modifiers> <indent> <statement_block> <dedent> |
define_stmt | Define <alias_name> (<selector_list>) |
capture_stmt | Capture <string> as <identifier> |
export_stmt | Export <identifier> |
action_stmt | <action_tokens> + optional <modifiers> + optional <action_block> |
comment | # <text> |
Action Rules
| Rule | Description |
|---|---|
action_tokens | One or more: identifier, hyphenated_identifier, string, or number |
action_block | Indented block containing capture_stmt or action_stmt items |
Modifier Rules
| Rule | Description |
|---|---|
_modifiers | Choice of: parens_modifiers or with_modifiers |
parens_modifiers | (<key_value_list>) |
with_modifiers | with <key_value_list> |
key_value_list | Comma-separated <key_value> pairs |
key_value | <identifier>: <value> |
selector_list | Comma-separated <key_value> pairs (used in define_stmt) |
Identifier and Literal Rules
| Rule | Description |
|---|---|
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.-]+ |
version | v\d+\.\d+(\.\d+)? (e.g., v1.0, v2.1.3) |
string | Single 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
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.