@rcrsr/rill 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -8
- package/dist/check/config.d.ts +20 -0
- package/dist/check/config.d.ts.map +1 -0
- package/dist/check/config.js +151 -0
- package/dist/check/config.js.map +1 -0
- package/dist/check/fixer.d.ts +39 -0
- package/dist/check/fixer.d.ts.map +1 -0
- package/dist/check/fixer.js +119 -0
- package/dist/check/fixer.js.map +1 -0
- package/dist/check/index.d.ts +10 -0
- package/dist/check/index.d.ts.map +1 -0
- package/dist/check/index.js +21 -0
- package/dist/check/index.js.map +1 -0
- package/dist/check/rules/anti-patterns.d.ts +65 -0
- package/dist/check/rules/anti-patterns.d.ts.map +1 -0
- package/dist/check/rules/anti-patterns.js +427 -0
- package/dist/check/rules/anti-patterns.js.map +1 -0
- package/dist/check/rules/closures.d.ts +66 -0
- package/dist/check/rules/closures.d.ts.map +1 -0
- package/dist/check/rules/closures.js +373 -0
- package/dist/check/rules/closures.js.map +1 -0
- package/dist/check/rules/collections.d.ts +90 -0
- package/dist/check/rules/collections.d.ts.map +1 -0
- package/dist/check/rules/collections.js +373 -0
- package/dist/check/rules/collections.js.map +1 -0
- package/dist/check/rules/conditionals.d.ts +41 -0
- package/dist/check/rules/conditionals.d.ts.map +1 -0
- package/dist/check/rules/conditionals.js +106 -0
- package/dist/check/rules/conditionals.js.map +1 -0
- package/dist/check/rules/flow.d.ts +46 -0
- package/dist/check/rules/flow.d.ts.map +1 -0
- package/dist/check/rules/flow.js +206 -0
- package/dist/check/rules/flow.js.map +1 -0
- package/dist/check/rules/formatting.d.ts +133 -0
- package/dist/check/rules/formatting.d.ts.map +1 -0
- package/dist/check/rules/formatting.js +639 -0
- package/dist/check/rules/formatting.js.map +1 -0
- package/dist/check/rules/helpers.d.ts +26 -0
- package/dist/check/rules/helpers.d.ts.map +1 -0
- package/dist/check/rules/helpers.js +66 -0
- package/dist/check/rules/helpers.js.map +1 -0
- package/dist/check/rules/index.d.ts +21 -0
- package/dist/check/rules/index.d.ts.map +1 -0
- package/dist/check/rules/index.js +78 -0
- package/dist/check/rules/index.js.map +1 -0
- package/dist/check/rules/loops.d.ts +70 -0
- package/dist/check/rules/loops.d.ts.map +1 -0
- package/dist/check/rules/loops.js +227 -0
- package/dist/check/rules/loops.js.map +1 -0
- package/dist/check/rules/naming.d.ts +21 -0
- package/dist/check/rules/naming.d.ts.map +1 -0
- package/dist/check/rules/naming.js +167 -0
- package/dist/check/rules/naming.js.map +1 -0
- package/dist/check/rules/strings.d.ts +28 -0
- package/dist/check/rules/strings.d.ts.map +1 -0
- package/dist/check/rules/strings.js +80 -0
- package/dist/check/rules/strings.js.map +1 -0
- package/dist/check/rules/types.d.ts +41 -0
- package/dist/check/rules/types.d.ts.map +1 -0
- package/dist/check/rules/types.js +162 -0
- package/dist/check/rules/types.js.map +1 -0
- package/dist/check/types.d.ts +106 -0
- package/dist/check/types.d.ts.map +1 -0
- package/dist/check/types.js +6 -0
- package/dist/check/types.js.map +1 -0
- package/dist/check/validator.d.ts +18 -0
- package/dist/check/validator.d.ts.map +1 -0
- package/dist/check/validator.js +88 -0
- package/dist/check/validator.js.map +1 -0
- package/dist/check/visitor.d.ts +33 -0
- package/dist/check/visitor.d.ts.map +1 -0
- package/dist/check/visitor.js +243 -0
- package/dist/check/visitor.js.map +1 -0
- package/dist/cli-check.d.ts +43 -0
- package/dist/cli-check.d.ts.map +1 -0
- package/dist/cli-check.js +356 -0
- package/dist/cli-check.js.map +1 -0
- package/dist/cli-eval.d.ts +15 -0
- package/dist/cli-eval.d.ts.map +1 -0
- package/dist/cli-eval.js +120 -0
- package/dist/cli-eval.js.map +1 -0
- package/dist/cli-exec.d.ts +49 -0
- package/dist/cli-exec.d.ts.map +1 -0
- package/dist/cli-exec.js +191 -0
- package/dist/cli-exec.js.map +1 -0
- package/dist/cli-module-loader.d.ts +19 -0
- package/dist/cli-module-loader.d.ts.map +1 -0
- package/dist/cli-module-loader.js +83 -0
- package/dist/cli-module-loader.js.map +1 -0
- package/dist/cli-shared.d.ts +36 -0
- package/dist/cli-shared.d.ts.map +1 -0
- package/dist/cli-shared.js +101 -0
- package/dist/cli-shared.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -11
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lexer/readers.d.ts +1 -1
- package/dist/lexer/readers.d.ts.map +1 -1
- package/dist/lexer/readers.js +62 -32
- package/dist/lexer/readers.js.map +1 -1
- package/dist/lexer/tokenizer.d.ts.map +1 -1
- package/dist/lexer/tokenizer.js +5 -6
- package/dist/lexer/tokenizer.js.map +1 -1
- package/dist/parser/index.js +1 -1
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/parser-expr.js +23 -5
- package/dist/parser/parser-expr.js.map +1 -1
- package/dist/parser/parser-functions.d.ts +2 -2
- package/dist/parser/parser-functions.d.ts.map +1 -1
- package/dist/parser/parser-functions.js +2 -1
- package/dist/parser/parser-functions.js.map +1 -1
- package/dist/parser/parser-literals.js +2 -2
- package/dist/parser/parser-literals.js.map +1 -1
- package/dist/parser/parser-script.js +9 -7
- package/dist/parser/parser-script.js.map +1 -1
- package/dist/parser/parser-variables.js +4 -3
- package/dist/parser/parser-variables.js.map +1 -1
- package/dist/runtime/core/callable.d.ts +5 -6
- package/dist/runtime/core/callable.d.ts.map +1 -1
- package/dist/runtime/core/callable.js.map +1 -1
- package/dist/runtime/core/context.d.ts.map +1 -1
- package/dist/runtime/core/context.js +19 -32
- package/dist/runtime/core/context.js.map +1 -1
- package/dist/runtime/core/equals.js +1 -1
- package/dist/runtime/core/equals.js.map +1 -1
- package/dist/runtime/core/eval/evaluator.d.ts +78 -0
- package/dist/runtime/core/eval/evaluator.d.ts.map +1 -1
- package/dist/runtime/core/eval/evaluator.js +78 -0
- package/dist/runtime/core/eval/evaluator.js.map +1 -1
- package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/closures.js +9 -1
- package/dist/runtime/core/eval/mixins/closures.js.map +1 -1
- package/dist/runtime/core/eval/mixins/variables.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/variables.js +143 -2
- package/dist/runtime/core/eval/mixins/variables.js.map +1 -1
- package/dist/runtime/core/types.d.ts +15 -2
- package/dist/runtime/core/types.d.ts.map +1 -1
- package/dist/runtime/core/types.js.map +1 -1
- package/dist/runtime/ext/extensions.d.ts +51 -0
- package/dist/runtime/ext/extensions.d.ts.map +1 -0
- package/dist/runtime/ext/extensions.js +67 -0
- package/dist/runtime/ext/extensions.js.map +1 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/index.js.map +1 -1
- package/dist/types.d.ts +8 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +5 -4
- package/dist/types.js.map +1 -1
- package/docs/00_INDEX.md +1 -0
- package/docs/01_guide.md +3 -3
- package/docs/02_types.md +8 -10
- package/docs/03_variables.md +10 -0
- package/docs/04_operators.md +3 -3
- package/docs/05_control-flow.md +21 -0
- package/docs/07_collections.md +2 -0
- package/docs/10_parsing.md +9 -9
- package/docs/11_reference.md +1 -1
- package/docs/12_examples.md +36 -62
- package/docs/14_host-integration.md +116 -111
- package/docs/15_grammar.ebnf +1 -5
- package/docs/16_conventions.md +3 -4
- package/docs/17_cli-tools.md +184 -0
- package/docs/99_llm-reference.txt +46 -5
- package/package.json +13 -4
- package/dist/demo.d.ts +0 -6
- package/dist/demo.d.ts.map +0 -1
- package/dist/demo.js +0 -121
- package/dist/demo.js.map +0 -1
- package/dist/lexer.d.ts +0 -19
- package/dist/lexer.d.ts.map +0 -1
- package/dist/lexer.js +0 -344
- package/dist/lexer.js.map +0 -1
- package/dist/parser/arithmetic.d.ts +0 -16
- package/dist/parser/arithmetic.d.ts.map +0 -1
- package/dist/parser/arithmetic.js +0 -128
- package/dist/parser/arithmetic.js.map +0 -1
- package/dist/parser/boolean.d.ts +0 -15
- package/dist/parser/boolean.d.ts.map +0 -1
- package/dist/parser/boolean.js +0 -20
- package/dist/parser/boolean.js.map +0 -1
- package/dist/parser/control-flow.d.ts +0 -56
- package/dist/parser/control-flow.d.ts.map +0 -1
- package/dist/parser/control-flow.js +0 -167
- package/dist/parser/control-flow.js.map +0 -1
- package/dist/parser/expressions.d.ts +0 -23
- package/dist/parser/expressions.d.ts.map +0 -1
- package/dist/parser/expressions.js +0 -950
- package/dist/parser/expressions.js.map +0 -1
- package/dist/parser/extraction.d.ts +0 -48
- package/dist/parser/extraction.d.ts.map +0 -1
- package/dist/parser/extraction.js +0 -279
- package/dist/parser/extraction.js.map +0 -1
- package/dist/parser/functions.d.ts +0 -20
- package/dist/parser/functions.d.ts.map +0 -1
- package/dist/parser/functions.js +0 -96
- package/dist/parser/functions.js.map +0 -1
- package/dist/parser/literals.d.ts +0 -37
- package/dist/parser/literals.d.ts.map +0 -1
- package/dist/parser/literals.js +0 -373
- package/dist/parser/literals.js.map +0 -1
- package/dist/parser/script.d.ts +0 -14
- package/dist/parser/script.d.ts.map +0 -1
- package/dist/parser/script.js +0 -196
- package/dist/parser/script.js.map +0 -1
- package/dist/parser/variables.d.ts +0 -10
- package/dist/parser/variables.d.ts.map +0 -1
- package/dist/parser/variables.js +0 -215
- package/dist/parser/variables.js.map +0 -1
- package/dist/runtime/ast-equals.d.ts +0 -13
- package/dist/runtime/ast-equals.d.ts.map +0 -1
- package/dist/runtime/ast-equals.js +0 -447
- package/dist/runtime/ast-equals.js.map +0 -1
- package/dist/runtime/builtins.d.ts +0 -13
- package/dist/runtime/builtins.d.ts.map +0 -1
- package/dist/runtime/builtins.js +0 -180
- package/dist/runtime/builtins.js.map +0 -1
- package/dist/runtime/callable.d.ts +0 -88
- package/dist/runtime/callable.d.ts.map +0 -1
- package/dist/runtime/callable.js +0 -98
- package/dist/runtime/callable.js.map +0 -1
- package/dist/runtime/context.d.ts +0 -13
- package/dist/runtime/context.d.ts.map +0 -1
- package/dist/runtime/context.js +0 -73
- package/dist/runtime/context.js.map +0 -1
- package/dist/runtime/core/evaluate.d.ts +0 -42
- package/dist/runtime/core/evaluate.d.ts.map +0 -1
- package/dist/runtime/core/evaluate.debug.js +0 -1251
- package/dist/runtime/core/evaluate.js +0 -1913
- package/dist/runtime/core/evaluate.js.map +0 -1
- package/dist/runtime/evaluate.d.ts +0 -32
- package/dist/runtime/evaluate.d.ts.map +0 -1
- package/dist/runtime/evaluate.js +0 -1111
- package/dist/runtime/evaluate.js.map +0 -1
- package/dist/runtime/execute.d.ts +0 -26
- package/dist/runtime/execute.d.ts.map +0 -1
- package/dist/runtime/execute.js +0 -121
- package/dist/runtime/execute.js.map +0 -1
- package/dist/runtime/signals.d.ts +0 -19
- package/dist/runtime/signals.d.ts.map +0 -1
- package/dist/runtime/signals.js +0 -26
- package/dist/runtime/signals.js.map +0 -1
- package/dist/runtime/types.d.ts +0 -169
- package/dist/runtime/types.d.ts.map +0 -1
- package/dist/runtime/types.js +0 -50
- package/dist/runtime/types.js.map +0 -1
- package/dist/runtime/values.d.ts +0 -50
- package/dist/runtime/values.d.ts.map +0 -1
- package/dist/runtime/values.js +0 -209
- package/dist/runtime/values.js.map +0 -1
- package/dist/runtime.d.ts +0 -254
- package/dist/runtime.d.ts.map +0 -1
- package/dist/runtime.js +0 -2014
- package/dist/runtime.js.map +0 -1
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anti-Pattern Rules
|
|
3
|
+
* Enforces best practices from docs/16_conventions.md:411-462.
|
|
4
|
+
*/
|
|
5
|
+
import { extractContextLine } from './helpers.js';
|
|
6
|
+
// ============================================================
|
|
7
|
+
// AVOID_REASSIGNMENT RULE
|
|
8
|
+
// ============================================================
|
|
9
|
+
/**
|
|
10
|
+
* Warns on variable reassignment patterns.
|
|
11
|
+
* Variables lock to their first type, and reassignment suggests confusing
|
|
12
|
+
* flow control. Prefer functional style or new variables.
|
|
13
|
+
*
|
|
14
|
+
* Detection:
|
|
15
|
+
* - Capture node (:> $var) where $var already exists in validation context
|
|
16
|
+
* - Tracks variables seen during validation pass
|
|
17
|
+
*
|
|
18
|
+
* Valid alternatives:
|
|
19
|
+
* - Use new variable: $result1, $result2
|
|
20
|
+
* - Functional chains: value -> op1 -> op2
|
|
21
|
+
*
|
|
22
|
+
* References:
|
|
23
|
+
* - docs/16_conventions.md:413-424
|
|
24
|
+
*/
|
|
25
|
+
export const AVOID_REASSIGNMENT = {
|
|
26
|
+
code: 'AVOID_REASSIGNMENT',
|
|
27
|
+
category: 'anti-patterns',
|
|
28
|
+
severity: 'warning',
|
|
29
|
+
nodeTypes: ['Capture'],
|
|
30
|
+
validate(node, context) {
|
|
31
|
+
const captureNode = node;
|
|
32
|
+
const varName = captureNode.name;
|
|
33
|
+
// Check if this variable was already captured before
|
|
34
|
+
if (context.variables.has(varName)) {
|
|
35
|
+
const firstLocation = context.variables.get(varName);
|
|
36
|
+
return [
|
|
37
|
+
{
|
|
38
|
+
location: captureNode.span.start,
|
|
39
|
+
severity: 'warning',
|
|
40
|
+
code: 'AVOID_REASSIGNMENT',
|
|
41
|
+
message: `Variable reassignment detected: '$${varName}' first defined at line ${firstLocation.line}. Prefer new variable or functional style.`,
|
|
42
|
+
context: extractContextLine(captureNode.span.start.line, context.source),
|
|
43
|
+
fix: null, // Cannot auto-fix without understanding intent
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
return [];
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
// ============================================================
|
|
51
|
+
// COMPLEX_CONDITION RULE
|
|
52
|
+
// ============================================================
|
|
53
|
+
/**
|
|
54
|
+
* Warns on complex nested boolean conditions.
|
|
55
|
+
* Complex conditions with multiple nested operators are hard to read.
|
|
56
|
+
* Extract to named variables for clarity.
|
|
57
|
+
*
|
|
58
|
+
* Detection:
|
|
59
|
+
* - Conditional nodes with conditions containing 3+ boolean operators (&&, ||)
|
|
60
|
+
* - Nesting depth > 2 for boolean expressions
|
|
61
|
+
*
|
|
62
|
+
* Valid alternatives:
|
|
63
|
+
* - Extract sub-conditions to named variables
|
|
64
|
+
* - Split complex checks into multiple smaller checks
|
|
65
|
+
*
|
|
66
|
+
* References:
|
|
67
|
+
* - docs/16_conventions.md:451-461
|
|
68
|
+
*/
|
|
69
|
+
export const COMPLEX_CONDITION = {
|
|
70
|
+
code: 'COMPLEX_CONDITION',
|
|
71
|
+
category: 'anti-patterns',
|
|
72
|
+
severity: 'info',
|
|
73
|
+
nodeTypes: ['Conditional'],
|
|
74
|
+
validate(node, context) {
|
|
75
|
+
const conditionalNode = node;
|
|
76
|
+
const condition = conditionalNode.condition;
|
|
77
|
+
if (!condition) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
// Unwrap GroupedExpr to get to the actual condition
|
|
81
|
+
let unwrappedCondition = condition;
|
|
82
|
+
if (unwrappedCondition.type === 'GroupedExpr') {
|
|
83
|
+
unwrappedCondition = unwrappedCondition.expression;
|
|
84
|
+
}
|
|
85
|
+
// Count boolean operators, boolean nesting depth, and parenthetical nesting
|
|
86
|
+
const operatorCount = countBooleanOperators(unwrappedCondition);
|
|
87
|
+
const booleanDepth = getBooleanNestingDepth(unwrappedCondition);
|
|
88
|
+
const parenDepth = getParenNestingDepth(unwrappedCondition);
|
|
89
|
+
// Flag if 3+ operators, boolean nesting > 2, or excessive parentheses (> 2)
|
|
90
|
+
if (operatorCount >= 3 || booleanDepth > 2 || parenDepth > 2) {
|
|
91
|
+
return [
|
|
92
|
+
{
|
|
93
|
+
location: conditionalNode.span.start,
|
|
94
|
+
severity: 'info',
|
|
95
|
+
code: 'COMPLEX_CONDITION',
|
|
96
|
+
message: 'Complex condition with multiple operators. Extract to named checks for clarity.',
|
|
97
|
+
context: extractContextLine(conditionalNode.span.start.line, context.source),
|
|
98
|
+
fix: null, // Auto-fix would require semantic understanding
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
return [];
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Count boolean operators (&&, ||) in an expression tree.
|
|
107
|
+
*/
|
|
108
|
+
function countBooleanOperators(node) {
|
|
109
|
+
let count = 0;
|
|
110
|
+
if (node.type === 'BinaryExpr') {
|
|
111
|
+
const binaryNode = node;
|
|
112
|
+
if (binaryNode.op === '&&' || binaryNode.op === '||') {
|
|
113
|
+
count = 1;
|
|
114
|
+
}
|
|
115
|
+
count += countBooleanOperators(binaryNode.left);
|
|
116
|
+
count += countBooleanOperators(binaryNode.right);
|
|
117
|
+
}
|
|
118
|
+
// Traverse other node types that might contain expressions
|
|
119
|
+
switch (node.type) {
|
|
120
|
+
case 'UnaryExpr': {
|
|
121
|
+
const unaryNode = node;
|
|
122
|
+
count += countBooleanOperators(unaryNode.operand);
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
case 'GroupedExpr': {
|
|
126
|
+
const groupedNode = node;
|
|
127
|
+
count += countBooleanOperators(groupedNode.expression);
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
case 'PipeChain': {
|
|
131
|
+
const pipeNode = node;
|
|
132
|
+
if (pipeNode.head)
|
|
133
|
+
count += countBooleanOperators(pipeNode.head);
|
|
134
|
+
if (pipeNode.pipes) {
|
|
135
|
+
for (const pipe of pipeNode.pipes) {
|
|
136
|
+
count += countBooleanOperators(pipe);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
case 'PostfixExpr': {
|
|
142
|
+
const postfixNode = node;
|
|
143
|
+
if (postfixNode.primary)
|
|
144
|
+
count += countBooleanOperators(postfixNode.primary);
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return count;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Calculate maximum nesting depth of boolean operators.
|
|
152
|
+
*/
|
|
153
|
+
function getBooleanNestingDepth(node, currentDepth = 0) {
|
|
154
|
+
let maxDepth = currentDepth;
|
|
155
|
+
if (node.type === 'BinaryExpr') {
|
|
156
|
+
const binaryNode = node;
|
|
157
|
+
const depth = binaryNode.op === '&&' || binaryNode.op === '||'
|
|
158
|
+
? currentDepth + 1
|
|
159
|
+
: currentDepth;
|
|
160
|
+
const leftDepth = getBooleanNestingDepth(binaryNode.left, depth);
|
|
161
|
+
const rightDepth = getBooleanNestingDepth(binaryNode.right, depth);
|
|
162
|
+
maxDepth = Math.max(maxDepth, leftDepth, rightDepth);
|
|
163
|
+
}
|
|
164
|
+
// Traverse other node types
|
|
165
|
+
switch (node.type) {
|
|
166
|
+
case 'UnaryExpr': {
|
|
167
|
+
const unaryNode = node;
|
|
168
|
+
maxDepth = Math.max(maxDepth, getBooleanNestingDepth(unaryNode.operand, currentDepth));
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
case 'GroupedExpr': {
|
|
172
|
+
const groupedNode = node;
|
|
173
|
+
maxDepth = Math.max(maxDepth, getBooleanNestingDepth(groupedNode.expression, currentDepth));
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
case 'PipeChain': {
|
|
177
|
+
const pipeNode = node;
|
|
178
|
+
if (pipeNode.head) {
|
|
179
|
+
maxDepth = Math.max(maxDepth, getBooleanNestingDepth(pipeNode.head, currentDepth));
|
|
180
|
+
}
|
|
181
|
+
if (pipeNode.pipes) {
|
|
182
|
+
for (const pipe of pipeNode.pipes) {
|
|
183
|
+
maxDepth = Math.max(maxDepth, getBooleanNestingDepth(pipe, currentDepth));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
case 'PostfixExpr': {
|
|
189
|
+
const postfixNode = node;
|
|
190
|
+
if (postfixNode.primary) {
|
|
191
|
+
maxDepth = Math.max(maxDepth, getBooleanNestingDepth(postfixNode.primary, currentDepth));
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return maxDepth;
|
|
197
|
+
}
|
|
198
|
+
// ============================================================
|
|
199
|
+
// LOOP_OUTER_CAPTURE RULE
|
|
200
|
+
// ============================================================
|
|
201
|
+
/**
|
|
202
|
+
* Detects attempts to modify outer-scope variables from inside loops.
|
|
203
|
+
* This is a common LLM-generated anti-pattern that never works in Rill.
|
|
204
|
+
*
|
|
205
|
+
* Rill's scoping rules mean that captures inside loop bodies create LOCAL
|
|
206
|
+
* variables that don't affect outer scope. This is a fundamental language
|
|
207
|
+
* constraint, not a style preference.
|
|
208
|
+
*
|
|
209
|
+
* WRONG - this pattern NEVER works:
|
|
210
|
+
* 0 :> $count
|
|
211
|
+
* [1, 2, 3] -> each { $count + 1 :> $count } # creates LOCAL $count
|
|
212
|
+
* $count # still 0!
|
|
213
|
+
*
|
|
214
|
+
* RIGHT - use accumulators:
|
|
215
|
+
* [1, 2, 3] -> fold(0) { $@ + 1 } # returns 3
|
|
216
|
+
* [1, 2, 3] -> each(0) { $@ + 1 } # returns [1, 2, 3]
|
|
217
|
+
*
|
|
218
|
+
* This rule catches captures inside loop/collection bodies where the
|
|
219
|
+
* variable name matches an outer-scope variable.
|
|
220
|
+
*
|
|
221
|
+
* References:
|
|
222
|
+
* - docs/99_llm-reference.txt (LOOP STATE PATTERNS)
|
|
223
|
+
* - docs/03_variables.md (Scope Rules)
|
|
224
|
+
*/
|
|
225
|
+
export const LOOP_OUTER_CAPTURE = {
|
|
226
|
+
code: 'LOOP_OUTER_CAPTURE',
|
|
227
|
+
category: 'anti-patterns',
|
|
228
|
+
severity: 'warning',
|
|
229
|
+
nodeTypes: [
|
|
230
|
+
'EachExpr',
|
|
231
|
+
'MapExpr',
|
|
232
|
+
'FilterExpr',
|
|
233
|
+
'FoldExpr',
|
|
234
|
+
'WhileLoop',
|
|
235
|
+
'DoWhileLoop',
|
|
236
|
+
],
|
|
237
|
+
validate(node, context) {
|
|
238
|
+
const diagnostics = [];
|
|
239
|
+
// Get the loop body based on node type
|
|
240
|
+
let body = null;
|
|
241
|
+
switch (node.type) {
|
|
242
|
+
case 'EachExpr':
|
|
243
|
+
body = node.body;
|
|
244
|
+
break;
|
|
245
|
+
case 'MapExpr':
|
|
246
|
+
body = node.body;
|
|
247
|
+
break;
|
|
248
|
+
case 'FilterExpr':
|
|
249
|
+
body = node.body;
|
|
250
|
+
break;
|
|
251
|
+
case 'FoldExpr':
|
|
252
|
+
body = node.body;
|
|
253
|
+
break;
|
|
254
|
+
case 'WhileLoop':
|
|
255
|
+
body = node.body;
|
|
256
|
+
break;
|
|
257
|
+
case 'DoWhileLoop':
|
|
258
|
+
body = node.body;
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
if (!body)
|
|
262
|
+
return diagnostics;
|
|
263
|
+
// Find all captures in the body
|
|
264
|
+
const captures = findCapturesInBody(body);
|
|
265
|
+
// Check if any capture targets an outer-scope variable
|
|
266
|
+
for (const capture of captures) {
|
|
267
|
+
if (context.variables.has(capture.name)) {
|
|
268
|
+
const outerLocation = context.variables.get(capture.name);
|
|
269
|
+
diagnostics.push({
|
|
270
|
+
location: capture.span.start,
|
|
271
|
+
severity: 'warning',
|
|
272
|
+
code: 'LOOP_OUTER_CAPTURE',
|
|
273
|
+
message: `Cannot modify outer variable '$${capture.name}' from inside loop. ` +
|
|
274
|
+
`Captures inside loops create LOCAL variables. ` +
|
|
275
|
+
`Use fold(init) with $@ accumulator, or pack state into $ as a dict. ` +
|
|
276
|
+
`(Outer '$${capture.name}' defined at line ${outerLocation.line})`,
|
|
277
|
+
context: extractContextLine(capture.span.start.line, context.source),
|
|
278
|
+
fix: null,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return diagnostics;
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
/**
|
|
286
|
+
* Recursively find all Capture nodes in a loop body.
|
|
287
|
+
*/
|
|
288
|
+
function findCapturesInBody(node) {
|
|
289
|
+
const captures = [];
|
|
290
|
+
function traverse(n) {
|
|
291
|
+
if (n.type === 'Capture') {
|
|
292
|
+
captures.push(n);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
// Traverse children based on node type
|
|
296
|
+
switch (n.type) {
|
|
297
|
+
case 'Block':
|
|
298
|
+
for (const stmt of n.statements)
|
|
299
|
+
traverse(stmt);
|
|
300
|
+
break;
|
|
301
|
+
case 'Statement':
|
|
302
|
+
traverse(n.expression);
|
|
303
|
+
break;
|
|
304
|
+
case 'AnnotatedStatement':
|
|
305
|
+
traverse(n.statement);
|
|
306
|
+
break;
|
|
307
|
+
case 'PipeChain':
|
|
308
|
+
traverse(n.head);
|
|
309
|
+
for (const pipe of n.pipes)
|
|
310
|
+
traverse(pipe);
|
|
311
|
+
if (n.terminator)
|
|
312
|
+
traverse(n.terminator);
|
|
313
|
+
break;
|
|
314
|
+
case 'PostfixExpr':
|
|
315
|
+
traverse(n.primary);
|
|
316
|
+
for (const method of n.methods)
|
|
317
|
+
traverse(method);
|
|
318
|
+
break;
|
|
319
|
+
case 'BinaryExpr':
|
|
320
|
+
traverse(n.left);
|
|
321
|
+
traverse(n.right);
|
|
322
|
+
break;
|
|
323
|
+
case 'UnaryExpr':
|
|
324
|
+
traverse(n.operand);
|
|
325
|
+
break;
|
|
326
|
+
case 'GroupedExpr':
|
|
327
|
+
traverse(n.expression);
|
|
328
|
+
break;
|
|
329
|
+
case 'Conditional':
|
|
330
|
+
if (n.input)
|
|
331
|
+
traverse(n.input);
|
|
332
|
+
if (n.condition)
|
|
333
|
+
traverse(n.condition);
|
|
334
|
+
traverse(n.thenBranch);
|
|
335
|
+
if (n.elseBranch)
|
|
336
|
+
traverse(n.elseBranch);
|
|
337
|
+
break;
|
|
338
|
+
case 'Closure':
|
|
339
|
+
// Don't traverse into closures - they have their own scope
|
|
340
|
+
break;
|
|
341
|
+
// Nested loops - traverse their bodies too
|
|
342
|
+
case 'WhileLoop':
|
|
343
|
+
traverse(n.body);
|
|
344
|
+
break;
|
|
345
|
+
case 'DoWhileLoop':
|
|
346
|
+
traverse(n.body);
|
|
347
|
+
break;
|
|
348
|
+
case 'EachExpr':
|
|
349
|
+
case 'MapExpr':
|
|
350
|
+
case 'FilterExpr':
|
|
351
|
+
case 'FoldExpr':
|
|
352
|
+
traverse(n.body);
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
traverse(node);
|
|
357
|
+
return captures;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Calculate maximum consecutive GroupedExpr (parenthetical) nesting depth.
|
|
361
|
+
* Counts chains of nested parentheses like ((($x))).
|
|
362
|
+
* Treats PipeChain (single head) and PostfixExpr (primary only) as transparent wrappers.
|
|
363
|
+
*/
|
|
364
|
+
function getParenNestingDepth(node) {
|
|
365
|
+
let maxDepth = 0;
|
|
366
|
+
function traverse(n, consecutiveDepth) {
|
|
367
|
+
if (n.type === 'GroupedExpr') {
|
|
368
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
369
|
+
const groupedNode = n;
|
|
370
|
+
const newDepth = consecutiveDepth + 1;
|
|
371
|
+
maxDepth = Math.max(maxDepth, newDepth);
|
|
372
|
+
traverse(groupedNode.expression, newDepth);
|
|
373
|
+
}
|
|
374
|
+
else if (n.type === 'PipeChain') {
|
|
375
|
+
// Treat simple PipeChain (head only) as transparent for nesting
|
|
376
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
377
|
+
const pipeNode = n;
|
|
378
|
+
if (pipeNode.head && (!pipeNode.pipes || pipeNode.pipes.length === 0)) {
|
|
379
|
+
// Transparent: pass through consecutive depth
|
|
380
|
+
traverse(pipeNode.head, consecutiveDepth);
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
// Complex pipe chain: reset depth but continue traversing
|
|
384
|
+
if (pipeNode.head)
|
|
385
|
+
traverse(pipeNode.head, 0);
|
|
386
|
+
if (pipeNode.pipes) {
|
|
387
|
+
for (const pipe of pipeNode.pipes) {
|
|
388
|
+
traverse(pipe, 0);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
else if (n.type === 'PostfixExpr') {
|
|
394
|
+
// Treat simple PostfixExpr (primary only) as transparent for nesting
|
|
395
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
396
|
+
const postfixNode = n;
|
|
397
|
+
if (postfixNode.primary &&
|
|
398
|
+
(!postfixNode.accessChain || postfixNode.accessChain.length === 0)) {
|
|
399
|
+
// Transparent: pass through consecutive depth
|
|
400
|
+
traverse(postfixNode.primary, consecutiveDepth);
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
// Complex postfix: reset depth
|
|
404
|
+
if (postfixNode.primary)
|
|
405
|
+
traverse(postfixNode.primary, 0);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
// Reset consecutive depth when we hit a structural node
|
|
410
|
+
// but continue traversing children
|
|
411
|
+
if (n.type === 'BinaryExpr') {
|
|
412
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
413
|
+
const binaryNode = n;
|
|
414
|
+
traverse(binaryNode.left, 0);
|
|
415
|
+
traverse(binaryNode.right, 0);
|
|
416
|
+
}
|
|
417
|
+
else if (n.type === 'UnaryExpr') {
|
|
418
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
419
|
+
const unaryNode = n;
|
|
420
|
+
traverse(unaryNode.operand, 0);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
traverse(node, 0);
|
|
425
|
+
return maxDepth;
|
|
426
|
+
}
|
|
427
|
+
//# sourceMappingURL=anti-patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anti-patterns.js","sourceRoot":"","sources":["../../../src/check/rules/anti-patterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Closure Convention Rules
|
|
3
|
+
* Enforces closure best practices from docs/16_conventions.md:237-286.
|
|
4
|
+
*/
|
|
5
|
+
import type { ValidationRule } from '../types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Warns on bare $ in stored closures without parameters.
|
|
8
|
+
* Bare $ in stored closures has ambiguous binding - it refers to the
|
|
9
|
+
* pipe value at closure invocation time, not definition time.
|
|
10
|
+
*
|
|
11
|
+
* Detection:
|
|
12
|
+
* - Zero-parameter closures (|| { }) used outside dict context
|
|
13
|
+
* - Body contains bare $ references (VariableNode with name '$')
|
|
14
|
+
*
|
|
15
|
+
* Valid patterns:
|
|
16
|
+
* - Dict closures: [count: ||{ $.items -> .len }] ($ binds to dict)
|
|
17
|
+
* - Parameterized closures: |x|{ $x } (explicit params)
|
|
18
|
+
* - Inline blocks: -> { $ * 2 } (immediate evaluation)
|
|
19
|
+
*
|
|
20
|
+
* References:
|
|
21
|
+
* - docs/16_conventions.md:251-261
|
|
22
|
+
* - docs/06_closures.md: Late binding section
|
|
23
|
+
*/
|
|
24
|
+
export declare const CLOSURE_BARE_DOLLAR: ValidationRule;
|
|
25
|
+
/**
|
|
26
|
+
* Enforces braces for complex closure bodies.
|
|
27
|
+
* Simple expressions can use parentheses, but complex bodies need braces.
|
|
28
|
+
*
|
|
29
|
+
* Complex body criteria:
|
|
30
|
+
* - Contains Block (multiple statements)
|
|
31
|
+
* - Contains Conditional
|
|
32
|
+
* - Contains loop constructs
|
|
33
|
+
*
|
|
34
|
+
* Simple bodies (parentheses OK):
|
|
35
|
+
* - Single expression: |x|($x * 2)
|
|
36
|
+
* - Single method chain: |s|($s.trim.lower)
|
|
37
|
+
*
|
|
38
|
+
* Complex bodies (braces required):
|
|
39
|
+
* - Conditionals: |n| { ($n < 1) ? 1 ! ($n * $fact($n - 1)) }
|
|
40
|
+
* - Multiple statements: |x| { $x :> $y; $y * 2 }
|
|
41
|
+
*
|
|
42
|
+
* References:
|
|
43
|
+
* - docs/16_conventions.md:239-249
|
|
44
|
+
*/
|
|
45
|
+
export declare const CLOSURE_BRACES: ValidationRule;
|
|
46
|
+
/**
|
|
47
|
+
* Detects closures created in loops that may suffer from late binding issues.
|
|
48
|
+
* When creating closures inside loops, variables are captured by reference,
|
|
49
|
+
* not by value. This causes all closures to share the final loop value.
|
|
50
|
+
*
|
|
51
|
+
* Detection:
|
|
52
|
+
* - Each loop body creates a Closure node
|
|
53
|
+
* - Closure references loop variable ($) without explicit capture
|
|
54
|
+
*
|
|
55
|
+
* Solution: Explicit capture per iteration:
|
|
56
|
+
* [1, 2, 3] -> each {
|
|
57
|
+
* $ :> $item
|
|
58
|
+
* || { $item }
|
|
59
|
+
* }
|
|
60
|
+
*
|
|
61
|
+
* References:
|
|
62
|
+
* - docs/16_conventions.md:251-261
|
|
63
|
+
* - docs/06_closures.md: Late binding section
|
|
64
|
+
*/
|
|
65
|
+
export declare const CLOSURE_LATE_BINDING: ValidationRule;
|
|
66
|
+
//# sourceMappingURL=closures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"closures.d.ts","sourceRoot":"","sources":["../../../src/check/rules/closures.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,aAAa,CAAC;AAarB;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,mBAAmB,EAAE,cAoCjC,CAAC;AAmIF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,cAAc,EAAE,cAsD5B,CAAC;AAMF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,oBAAoB,EAAE,cAqClC,CAAC"}
|