fix(tree-sitter): resolve if keyword ambiguity in behavior nodes

Separate if(expr) condition from if(expr) { child } decorator to fix
grammar conflicts. Simplify decorator_params to accept only integer,
range, or duration.
This commit is contained in:
2026-02-13 20:21:31 +00:00
parent b100ded3d3
commit 80332971b8
4 changed files with 6531 additions and 4672 deletions

View File

@@ -130,7 +130,7 @@ module.exports = grammar({
time: $ => /[0-9]{2}:[0-9]{2}(:[0-9]{2})?/,
duration: $ => /[0-9]+[hms]([0-9]+[hms])*/,
duration: $ => /[0-9]+[dhms]([0-9]+[dhms])*/,
list: $ => seq('[', commaSep($.value), ']'),
@@ -232,17 +232,83 @@ module.exports = grammar({
behavior_node: $ => choice(
$.selector_node,
$.sequence_node,
$.repeat_node,
$.condition_node,
$.if_decorator_node,
$.decorator_node,
$.action_node,
$.subtree_node
),
selector_node: $ => seq('?', '{', repeat1($.behavior_node), '}'),
// Selector node: choose { ... }
selector_node: $ => seq(
'choose',
optional(field('label', $.identifier)),
'{',
repeat1($.behavior_node),
'}'
),
sequence_node: $ => seq('>', '{', repeat1($.behavior_node), '}'),
// Sequence node: then { ... }
sequence_node: $ => seq(
'then',
optional(field('label', $.identifier)),
'{',
repeat1($.behavior_node),
'}'
),
repeat_node: $ => seq('*', '{', $.behavior_node, '}'),
// Condition node: if(expr) or when(expr) - NO BRACES
condition_node: $ => seq(
choice('if', 'when'),
'(',
field('condition', $.expression),
')'
),
// If decorator: if(expr) { child } - WITH BRACES
if_decorator_node: $ => seq(
'if',
'(',
field('condition', $.expression),
')',
'{',
field('child', $.behavior_node),
'}'
),
// Decorator node: repeat/retry/timeout/etc { child }
decorator_node: $ => seq(
field('decorator', $.decorator_keyword),
optional(field('params', $.decorator_params)),
'{',
field('child', $.behavior_node),
'}'
),
decorator_keyword: $ => choice(
'repeat',
'invert',
'retry',
'timeout',
'cooldown',
'succeed_always',
'fail_always'
),
decorator_params: $ => seq(
'(',
choice(
// min..max range (for repeat)
seq($.integer, '..', $.integer),
// N (for repeat, retry)
$.integer,
// duration (for timeout, cooldown)
$.duration
),
')'
),
// Action node: action_name or action_name(params)
action_node: $ => choice(
seq($.identifier, '(', commaSep($.action_param), ')'),
$.identifier
@@ -255,7 +321,8 @@ module.exports = grammar({
$.value
),
subtree_node: $ => seq('@', $.path),
// Subtree node: include path::to::subtree
subtree_node: $ => seq('include', $.path),
// Institution declaration
institution: $ => seq(
@@ -275,12 +342,10 @@ module.exports = grammar({
),
participant: $ => choice(
// name { fields }
seq($.path, $.block),
// name as role { fields }
// name as role { fields } (role optional)
seq($.path, 'as', $.identifier, $.block),
// bare name
$.path
// name { fields } (block required)
seq($.path, $.block)
),
// Location declaration