1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//! Bash: the logical connectives and the string comparison of a
//! `[ … ]` / `[[ … ]]` test.
use crateBash;
use crateNode;
/// The `&&` / `||` a `[ … ]` connective stands for: `Some(AMPAMP)` for
/// `-a` and `Some(PIPEPIPE)` for `-o`, when `op`, a child of `parent`,
/// is one.
///
/// tree-sitter-bash gives every `-x` operator the one kind
/// `test_operator`, so the text decides. Only a binary position joins
/// two tests: the unary `-a FILE` (a file-exists test) has a
/// `unary_expression` parent and is a comparison like `-f FILE`. Every
/// expression kind is matched by name, not by `kind_id`, because the
/// grammar aliases `binary_expression` five times (grammar-dispatch §1).
///
/// The `test` builtin spelling (`test "$a" = 1 -a "$b" = 2`) is out of
/// reach: the grammar parses its arguments as plain `word`s, so neither
/// its comparisons nor its connectives exist as nodes, and it scores as
/// one command.
/// How many `=` string comparisons `test`, a `[ … ]` / `[[ … ]]`
/// `test_command`, holds.
///
/// Both spellings of `=` are a `binary_expression`: `[ "$a" = 1 ]`
/// compares and `(( x = 1 ))` assigns. So an `=` compares exactly when
/// the test reaches it through expression nodes alone. The descent
/// passes through `binary_expression` because tree-sitter-bash nests `=`
/// wrongly — `[ "$a" = 1 -a "$b" = 2 ]` parses as
/// `"$a" = (1 -a "$b") = 2` — and through the unary and parenthesized
/// forms (`[[ ! ( "$a" = 1 ) ]]`). It stops at anything else, so the
/// assignment in `[[ $(( x = 1 )) -eq 1 ]]` is not counted.
///
/// Counted from the test down rather than by climbing from each `=` to
/// its test: on the mis-nested spine a climb per `=` is `O(depth)`, and a
/// long `[ … = … -o … ]` made the walk quadratic. The descent is an
/// explicit stack, since that spine is as deep as the test is long.