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
68
69
70
71
72
73
74
75
76
77
78
79
//! Tcl: the leading word of a `command` node, and the grammar's
//! braced-word kind ids.
use crateTcl;
use crateBracedWordKinds;
use crateNode;
/// The braced-word kinds `Getter::is_subsumed_braced_word`,
/// `Getter::braced_word_op_type` and `Getter::is_braced_script_word` are
/// instantiated with (#1354, #1318): the literal *value* form the guard
/// keys on, the *script* form it gates on holding a command, the comment
/// kind that gate must not mistake for one, the four kinds #1318's role
/// recognition walks — the generic `command`, its `word_list` argument
/// list, the `simple_word` a resolvable command name is spelled with, and
/// the `argument` whose braced child is a parameter default rather than a
/// script — and the `procedure` and `namespace` constructs whose value
/// slots the string and dump classifiers also recognise (#1381).
///
/// It lives here rather than beside the `Getter` impl because three
/// classifiers now read it — `Getter::get_op_type_with_code`,
/// `Checker::is_string_with_code` and `Alterator::keeps_children` — and
/// a second copy is exactly the drift `lang_helpers` exists to prevent
/// (#1381).
///
/// `Namespace`, not `Namespace2`: both are spelled `namespace`, and the
/// suffixed variant is the keyword token inside the construct.
pub const BRACED_WORD_KINDS: BracedWordKinds = BracedWordKinds ;
/// Reads the leading word of a Tcl `command` node when it is a plain
/// `simple_word` (`switch`, `for`, `puts`, …). Returns `None` for any other
/// node kind, for commands whose leading word is computed (`$cmd`, `[cmd]`
/// parse it as `variable_substitution` / `command_substitution`, never
/// statically resolvable to a builtin), and for non-UTF-8 bytes. Shared by
/// the out-of-band control-flow detectors below (grammar-dispatch §10:
/// identity questions read the bytes).
///
/// A *literal* name in a quoted or braced spelling is also unresolved, and
/// that is a deliberate limitation: `"for" {set i 0} {$i < 3} {incr i} {…}`
/// and `{for} …` are legal Tcl that still invoke the builtin, but the
/// grammar parses their name as `quoted_word` / `braced_word` rather than
/// `simple_word`, so they score as plain commands. The `simple_word` gate
/// is what keeps the computed forms out; matching the quoted spellings
/// would mean unquoting the bytes for a style no real Tcl uses.
///
/// Callers dispatch on the returned name so each `command` node resolves it
/// exactly once per metric walk — the helpers below take the resolved
/// identity as a precondition rather than re-deriving it.
///
/// The name is normalised through
/// [`strip_global_qualifier`](crate::lang_helpers::strip_global_qualifier),
/// so `::switch` resolves as `switch` — the same rule
/// `Getter::command_leading_word` applies on the slot-table side. Without
/// it the two disagreed on identical bytes: Halstead and the dump read a
/// qualified `::switch` as a script while cognitive, cyclomatic, ABC and
/// nexits read it as an ordinary call and scored it zero.