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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//! `Getter` implementation for iRules.
#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
use super::*;
impl Getter for IrulesCode {
fn get_space_kind(node: &Node) -> SpaceKind {
match node.kind_id().into() {
// Event handlers and procs are the function units (see the
// `IrulesCode` Checker impl for the handler-as-function rationale).
Irules::Procedure | Irules::WhenEvent | Irules::OnHandler | Irules::TrapHandler => {
SpaceKind::Function
}
Irules::SourceFile => SpaceKind::Unit,
_ => SpaceKind::Unknown,
}
}
fn get_op_type(node: &Node) -> HalsteadType {
match node.kind_id().into() {
// Anonymous keyword tokens (the `*2` aliases are the keyword
// literals; the unsuffixed high-id variants are the statement
// nodes counted by the branching metrics, not here).
Irules::Proc
| Irules::When
| Irules::On
| Irules::Off
| Irules::Trap
| Irules::If2
| Irules::Elseif2
| Irules::Else2
| Irules::While2
| Irules::For2
| Irules::Foreach2
| Irules::Switch2
| Irules::Set2
| Irules::Global2
| Irules::Namespace2
| Irules::Try2
| Irules::Catch2
| Irules::Finally2
| Irules::Regexp2
| Irules::Expr2
| Irules::Dict
| Irules::Update
| Irules::With
// String comparison operators (`eq`, `contains`, `matches`, …):
// operators, not branches (the branching metrics ignore them).
| Irules::Eq
| Irules::Ne
| Irules::StartsWith
| Irules::EndsWith
| Irules::Contains
| Irules::Equals
| Irules::Matches
| Irules::MatchesRegex
| Irules::MatchesGlob
| Irules::In
| Irules::Ni
// Structural punctuation. Only the *opening* delimiter is an
// operator (the pair folds to one glyph in
// `get_operator_id_as_str`); counting the matching closer too
// (former `RBRACE`/`RBRACK`/`RPAREN` arms) double-counted every
// balanced pair, inflating n1/N1 (#695).
// `LPAREN2` is defensive — the runtime collapses it to `LPAREN`
// before `kind_id()`, so it never fires (#768; see the Cpp note).
| Irules::LBRACE
| Irules::LBRACK
| Irules::LPAREN
| Irules::LPAREN2
| Irules::SEMI
| Irules::COLON
| Irules::COLONCOLON
| Irules::NsDelim
// Arithmetic / exponent operators.
| Irules::PLUS
| Irules::DASH
| Irules::STAR
| Irules::SLASH
| Irules::PERCENT
| Irules::STARSTAR
// Bitwise operators.
| Irules::AMP
| Irules::PIPE
| Irules::CARET
| Irules::TILDE
| Irules::LTLT
| Irules::GTGT
// Comparison operators.
| Irules::EQEQ
| Irules::BANGEQ
| Irules::LT
| Irules::GT
| Irules::LTEQ
| Irules::GTEQ
// Logical operators (symbolic and keyword forms).
| Irules::BANG
| Irules::Not
| Irules::AMPAMP
| Irules::And
| Irules::PIPEPIPE
| Irules::Or
// Ternary conditional operator.
| Irules::QMARK => HalsteadType::Operator,
// `Id` (named, id 49) is a standalone identifier operand — e.g.
// a `set` target (`set s …` → `s`). But it is ALSO the inner
// leaf of every `variable_substitution` (`$s` → `(variable_
// substitution (id "s"))`). The `VariableSubstitution` node is
// already the operand for the reference, so counting its inner
// `Id` too would double-count every `$var` (inflating n2/N2 and
// every derived Halstead value). Exclude `Id` exactly in that
// position. NB: unlike Tcl — whose var-sub leaf is the anonymous
// `Id2` token (so a blanket `Id2` exclusion sufficed) — iRules'
// grammar emits the *named* `Id` there, so the guard must be a
// parent check, not a kind exclusion (`Id2` here is a different,
// non-surfacing token).
Irules::Id => {
if node
.parent()
.is_some_and(|p| p.kind_id() == Irules::VariableSubstitution as u16)
{
HalsteadType::Unknown
} else {
HalsteadType::Operand
}
}
// Operands: identifiers and literals.
Irules::SimpleWord
| Irules::Number
| Irules::Boolean
| Irules::EventName
| Irules::BracedWord
| Irules::BracedWordSimple
| Irules::ArrayIndex
| Irules::VariableSubstitution => HalsteadType::Operand,
// Double-quoted strings count as a single operand when inert
// (`"hello world"`). When they carry a `$var` or `[cmd]`
// interpolation child, the inner substitution nodes are walked
// separately and contribute their own operands; counting the
// wrapping `QuotedWord` too would double-count `N2` (same pattern
// as Tcl #277 / Bash #180 / C# #183 / PHP #184).
Irules::QuotedWord => Self::string_operand_type(
node,
&[
Irules::VariableSubstitution as u16,
Irules::CommandSubstitution as u16,
],
),
_ => HalsteadType::Unknown,
}
}
get_operator!(Irules);
}