@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,373 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collection Operator Rules
|
|
3
|
+
* Enforces conventions for each, map, fold, and filter operators.
|
|
4
|
+
*/
|
|
5
|
+
import { extractContextLine } from './helpers.js';
|
|
6
|
+
// ============================================================
|
|
7
|
+
// HELPER FUNCTIONS
|
|
8
|
+
// ============================================================
|
|
9
|
+
/**
|
|
10
|
+
* Check if an AST subtree contains a Break node.
|
|
11
|
+
* Recursively traverses all node types.
|
|
12
|
+
*/
|
|
13
|
+
function containsBreak(node) {
|
|
14
|
+
if (node.type === 'Break') {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
// Recursively check children based on node type
|
|
18
|
+
switch (node.type) {
|
|
19
|
+
case 'Block':
|
|
20
|
+
return node.statements.some((stmt) => containsBreak(stmt));
|
|
21
|
+
case 'Statement':
|
|
22
|
+
return containsBreak(node.expression);
|
|
23
|
+
case 'AnnotatedStatement':
|
|
24
|
+
return containsBreak(node.statement);
|
|
25
|
+
case 'PipeChain':
|
|
26
|
+
if (containsBreak(node.head))
|
|
27
|
+
return true;
|
|
28
|
+
if (node.pipes.some((pipe) => containsBreak(pipe)))
|
|
29
|
+
return true;
|
|
30
|
+
if (node.terminator && node.terminator.type === 'Break')
|
|
31
|
+
return true;
|
|
32
|
+
return false;
|
|
33
|
+
case 'PostfixExpr':
|
|
34
|
+
if (containsBreak(node.primary))
|
|
35
|
+
return true;
|
|
36
|
+
return node.methods.some((method) => containsBreak(method));
|
|
37
|
+
case 'BinaryExpr':
|
|
38
|
+
return containsBreak(node.left) || containsBreak(node.right);
|
|
39
|
+
case 'UnaryExpr':
|
|
40
|
+
return containsBreak(node.operand);
|
|
41
|
+
case 'GroupedExpr':
|
|
42
|
+
return containsBreak(node.expression);
|
|
43
|
+
case 'Conditional':
|
|
44
|
+
if (node.input && containsBreak(node.input))
|
|
45
|
+
return true;
|
|
46
|
+
if (node.condition && containsBreak(node.condition))
|
|
47
|
+
return true;
|
|
48
|
+
if (containsBreak(node.thenBranch))
|
|
49
|
+
return true;
|
|
50
|
+
if (node.elseBranch && containsBreak(node.elseBranch))
|
|
51
|
+
return true;
|
|
52
|
+
return false;
|
|
53
|
+
case 'WhileLoop':
|
|
54
|
+
case 'DoWhileLoop':
|
|
55
|
+
return containsBreak(node.body);
|
|
56
|
+
case 'Closure':
|
|
57
|
+
return containsBreak(node.body);
|
|
58
|
+
case 'EachExpr':
|
|
59
|
+
case 'MapExpr':
|
|
60
|
+
case 'FoldExpr':
|
|
61
|
+
case 'FilterExpr':
|
|
62
|
+
return containsBreak(node.body);
|
|
63
|
+
case 'HostCall':
|
|
64
|
+
case 'ClosureCall':
|
|
65
|
+
case 'MethodCall':
|
|
66
|
+
case 'Invoke':
|
|
67
|
+
case 'PipeInvoke':
|
|
68
|
+
return node.args.some((arg) => containsBreak(arg));
|
|
69
|
+
case 'StringLiteral':
|
|
70
|
+
return node.parts.some((part) => typeof part !== 'string' && containsBreak(part));
|
|
71
|
+
case 'Tuple':
|
|
72
|
+
return node.elements.some((elem) => containsBreak(elem));
|
|
73
|
+
case 'Dict':
|
|
74
|
+
return node.entries.some((entry) => containsBreak(entry));
|
|
75
|
+
case 'DictEntry':
|
|
76
|
+
return containsBreak(node.value);
|
|
77
|
+
default:
|
|
78
|
+
// Leaf nodes and other types don't contain breaks
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if a body is a simple method shorthand.
|
|
84
|
+
* Body structure for .method shorthand is PostfixExpr with MethodCall as primary.
|
|
85
|
+
* Examples: .upper, .len, .trim
|
|
86
|
+
*/
|
|
87
|
+
function isMethodShorthand(body) {
|
|
88
|
+
if (body.type !== 'PostfixExpr')
|
|
89
|
+
return false;
|
|
90
|
+
return body.primary.type === 'MethodCall';
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Check if a body is a block wrapping a single method call on $.
|
|
94
|
+
* Example: { $.upper() } when it could be .upper
|
|
95
|
+
* Structure: Block -> Statement -> PipeChain -> PostfixExpr($) with methods
|
|
96
|
+
*/
|
|
97
|
+
function isBlockWrappingMethod(body) {
|
|
98
|
+
if (body.type !== 'Block')
|
|
99
|
+
return false;
|
|
100
|
+
if (body.statements.length !== 1)
|
|
101
|
+
return false;
|
|
102
|
+
const stmt = body.statements[0];
|
|
103
|
+
if (!stmt || stmt.type !== 'Statement')
|
|
104
|
+
return false;
|
|
105
|
+
const expr = stmt.expression;
|
|
106
|
+
if (expr.type !== 'PipeChain')
|
|
107
|
+
return false;
|
|
108
|
+
// Should have no pipes (direct method call on head)
|
|
109
|
+
if (expr.pipes.length !== 0)
|
|
110
|
+
return false;
|
|
111
|
+
const head = expr.head;
|
|
112
|
+
if (head.type !== 'PostfixExpr')
|
|
113
|
+
return false;
|
|
114
|
+
// Primary should be pipe variable ($)
|
|
115
|
+
if (head.primary.type !== 'Variable')
|
|
116
|
+
return false;
|
|
117
|
+
const variable = head.primary;
|
|
118
|
+
if (!('isPipeVar' in variable) || !variable.isPipeVar)
|
|
119
|
+
return false;
|
|
120
|
+
// Should have exactly one method in the methods array
|
|
121
|
+
if (head.methods.length !== 1)
|
|
122
|
+
return false;
|
|
123
|
+
if (head.methods[0]?.type !== 'MethodCall')
|
|
124
|
+
return false;
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get method name from iterator body.
|
|
129
|
+
* Handles both PostfixExpr (shorthand) and BlockNode (wrapped) forms.
|
|
130
|
+
*/
|
|
131
|
+
function getMethodName(body) {
|
|
132
|
+
// Shorthand form: PostfixExpr with MethodCall primary
|
|
133
|
+
if (body.type === 'PostfixExpr' && body.primary.type === 'MethodCall') {
|
|
134
|
+
return body.primary.name;
|
|
135
|
+
}
|
|
136
|
+
// Block form: $.method()
|
|
137
|
+
if (isBlockWrappingMethod(body)) {
|
|
138
|
+
const stmt = body.statements[0];
|
|
139
|
+
if (!stmt || stmt.type !== 'Statement')
|
|
140
|
+
return null;
|
|
141
|
+
const expr = stmt.expression;
|
|
142
|
+
if (expr.type !== 'PipeChain')
|
|
143
|
+
return null;
|
|
144
|
+
const head = expr.head;
|
|
145
|
+
if (head.type !== 'PostfixExpr')
|
|
146
|
+
return null;
|
|
147
|
+
const method = head.methods[0];
|
|
148
|
+
if (method && method.type === 'MethodCall') {
|
|
149
|
+
return method.name;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
// ============================================================
|
|
155
|
+
// BREAK_IN_PARALLEL RULE
|
|
156
|
+
// ============================================================
|
|
157
|
+
/**
|
|
158
|
+
* Validates that break is not used in parallel operators (map, filter).
|
|
159
|
+
*
|
|
160
|
+
* Break is semantically invalid in parallel execution contexts:
|
|
161
|
+
* - map: executes in parallel, no iteration order
|
|
162
|
+
* - filter: parallel predicate evaluation
|
|
163
|
+
*
|
|
164
|
+
* Break is valid in sequential operators:
|
|
165
|
+
* - each: sequential iteration with early termination
|
|
166
|
+
* - fold: sequential reduction (though uncommon)
|
|
167
|
+
*
|
|
168
|
+
* Error severity because this is semantically wrong, not just stylistic.
|
|
169
|
+
*
|
|
170
|
+
* References:
|
|
171
|
+
* - docs/16_conventions.md:90-149
|
|
172
|
+
* - docs/07_collections.md
|
|
173
|
+
*/
|
|
174
|
+
export const BREAK_IN_PARALLEL = {
|
|
175
|
+
code: 'BREAK_IN_PARALLEL',
|
|
176
|
+
category: 'collections',
|
|
177
|
+
severity: 'error',
|
|
178
|
+
nodeTypes: ['MapExpr', 'FilterExpr'],
|
|
179
|
+
validate(node, context) {
|
|
180
|
+
const collectionExpr = node;
|
|
181
|
+
const operatorName = node.type === 'MapExpr' ? 'map' : 'filter';
|
|
182
|
+
if (containsBreak(collectionExpr.body)) {
|
|
183
|
+
return [
|
|
184
|
+
{
|
|
185
|
+
location: node.span.start,
|
|
186
|
+
severity: 'error',
|
|
187
|
+
code: 'BREAK_IN_PARALLEL',
|
|
188
|
+
message: `Break not allowed in '${operatorName}' (parallel operator). Use 'each' for sequential iteration with break.`,
|
|
189
|
+
context: extractContextLine(node.span.start.line, context.source),
|
|
190
|
+
fix: null, // Cannot auto-fix operator replacement
|
|
191
|
+
},
|
|
192
|
+
];
|
|
193
|
+
}
|
|
194
|
+
return [];
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
// ============================================================
|
|
198
|
+
// PREFER_MAP RULE
|
|
199
|
+
// ============================================================
|
|
200
|
+
/**
|
|
201
|
+
* Suggests using map over each when no side effects are present.
|
|
202
|
+
*
|
|
203
|
+
* Map is semantically clearer for pure transformations:
|
|
204
|
+
* - Signals no side effects (parallel execution)
|
|
205
|
+
* - Better performance potential
|
|
206
|
+
* - More functional style
|
|
207
|
+
*
|
|
208
|
+
* Detects each expressions where:
|
|
209
|
+
* - Body doesn't reference accumulator ($@)
|
|
210
|
+
* - No accumulator initialization
|
|
211
|
+
* - Body doesn't contain side-effecting operations (host calls, logging)
|
|
212
|
+
*
|
|
213
|
+
* This is informational - both work, but map is clearer for pure transforms.
|
|
214
|
+
*
|
|
215
|
+
* References:
|
|
216
|
+
* - docs/16_conventions.md:90-149
|
|
217
|
+
*/
|
|
218
|
+
export const PREFER_MAP = {
|
|
219
|
+
code: 'PREFER_MAP',
|
|
220
|
+
category: 'collections',
|
|
221
|
+
severity: 'info',
|
|
222
|
+
nodeTypes: ['EachExpr'],
|
|
223
|
+
validate(node, context) {
|
|
224
|
+
const eachExpr = node;
|
|
225
|
+
// If accumulator is present, each is correct choice
|
|
226
|
+
if (eachExpr.accumulator !== null) {
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
// Check if body is a closure with accumulator parameter
|
|
230
|
+
if (eachExpr.body.type === 'Closure') {
|
|
231
|
+
const closure = eachExpr.body;
|
|
232
|
+
const hasAccumulator = closure.params.length > 1;
|
|
233
|
+
if (hasAccumulator) {
|
|
234
|
+
return [];
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// Simple heuristic: if body is pure (no side effects), suggest map
|
|
238
|
+
// For now, suggest map for simple transformations
|
|
239
|
+
// Full implementation would check for host calls, logging, etc.
|
|
240
|
+
return [
|
|
241
|
+
{
|
|
242
|
+
location: node.span.start,
|
|
243
|
+
severity: 'info',
|
|
244
|
+
code: 'PREFER_MAP',
|
|
245
|
+
message: "Consider using 'map' instead of 'each' for pure transformations (no side effects)",
|
|
246
|
+
context: extractContextLine(node.span.start.line, context.source),
|
|
247
|
+
fix: null, // Could generate fix by replacing 'each' with 'map'
|
|
248
|
+
},
|
|
249
|
+
];
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
// ============================================================
|
|
253
|
+
// FOLD_INTERMEDIATES RULE
|
|
254
|
+
// ============================================================
|
|
255
|
+
/**
|
|
256
|
+
* Suggests using fold for final-only results, each(init) for running totals.
|
|
257
|
+
*
|
|
258
|
+
* Semantic distinction:
|
|
259
|
+
* - fold: returns final accumulated value only
|
|
260
|
+
* - each(init): returns list of all intermediate results
|
|
261
|
+
*
|
|
262
|
+
* Detects patterns that might benefit from one or the other:
|
|
263
|
+
* - fold used when intermediate results might be needed
|
|
264
|
+
* - each(init) used when only final result matters
|
|
265
|
+
*
|
|
266
|
+
* This is informational - helps users choose the right operator.
|
|
267
|
+
*
|
|
268
|
+
* References:
|
|
269
|
+
* - docs/16_conventions.md:90-149
|
|
270
|
+
* - docs/07_collections.md
|
|
271
|
+
*/
|
|
272
|
+
export const FOLD_INTERMEDIATES = {
|
|
273
|
+
code: 'FOLD_INTERMEDIATES',
|
|
274
|
+
category: 'collections',
|
|
275
|
+
severity: 'info',
|
|
276
|
+
nodeTypes: ['EachExpr', 'FoldExpr'],
|
|
277
|
+
validate(_node, _context) {
|
|
278
|
+
// This rule is informational and would require flow analysis
|
|
279
|
+
// to detect whether intermediate values are used.
|
|
280
|
+
// Placeholder for future implementation.
|
|
281
|
+
return [];
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
// ============================================================
|
|
285
|
+
// FILTER_NEGATION RULE
|
|
286
|
+
// ============================================================
|
|
287
|
+
/**
|
|
288
|
+
* Validates that negation in filter uses grouped form.
|
|
289
|
+
*
|
|
290
|
+
* Grouped negation is clearer and prevents bugs:
|
|
291
|
+
* - Correct: filter (!.empty) -- grouped negation
|
|
292
|
+
* - Wrong: filter .empty -- filters for empty elements (likely bug)
|
|
293
|
+
*
|
|
294
|
+
* The ungrouped form .empty would return truthy elements,
|
|
295
|
+
* which is likely not intended when filtering.
|
|
296
|
+
*
|
|
297
|
+
* References:
|
|
298
|
+
* - docs/16_conventions.md:90-149
|
|
299
|
+
*/
|
|
300
|
+
export const FILTER_NEGATION = {
|
|
301
|
+
code: 'FILTER_NEGATION',
|
|
302
|
+
category: 'collections',
|
|
303
|
+
severity: 'warning',
|
|
304
|
+
nodeTypes: ['FilterExpr'],
|
|
305
|
+
validate(node, context) {
|
|
306
|
+
const filterExpr = node;
|
|
307
|
+
const body = filterExpr.body;
|
|
308
|
+
// Check if body is a simple method call (ungrouped)
|
|
309
|
+
if (isMethodShorthand(body)) {
|
|
310
|
+
const methodName = getMethodName(body);
|
|
311
|
+
// Check if method is likely a negation-intended method
|
|
312
|
+
// Common methods that might indicate user wants to negate:
|
|
313
|
+
// .empty, .is_match, etc.
|
|
314
|
+
if (methodName === 'empty') {
|
|
315
|
+
return [
|
|
316
|
+
{
|
|
317
|
+
location: node.span.start,
|
|
318
|
+
severity: 'warning',
|
|
319
|
+
code: 'FILTER_NEGATION',
|
|
320
|
+
message: `Filter with '.${methodName}' likely unintended. Use grouped negation: 'filter (!.${methodName})' to filter non-${methodName} elements`,
|
|
321
|
+
context: extractContextLine(node.span.start.line, context.source),
|
|
322
|
+
fix: null, // Could generate fix wrapping in (!...)
|
|
323
|
+
},
|
|
324
|
+
];
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return [];
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
// ============================================================
|
|
331
|
+
// METHOD_SHORTHAND RULE
|
|
332
|
+
// ============================================================
|
|
333
|
+
/**
|
|
334
|
+
* Suggests using method shorthand over block form in collection operators.
|
|
335
|
+
*
|
|
336
|
+
* Method shorthand is more concise and clearer:
|
|
337
|
+
* - Preferred: map .upper
|
|
338
|
+
* - Verbose: map { $.upper() }
|
|
339
|
+
*
|
|
340
|
+
* Detects block forms that wrap a single method call and suggests shorthand.
|
|
341
|
+
*
|
|
342
|
+
* This is informational - both forms work identically.
|
|
343
|
+
*
|
|
344
|
+
* References:
|
|
345
|
+
* - docs/16_conventions.md:90-149
|
|
346
|
+
*/
|
|
347
|
+
export const METHOD_SHORTHAND = {
|
|
348
|
+
code: 'METHOD_SHORTHAND',
|
|
349
|
+
category: 'collections',
|
|
350
|
+
severity: 'info',
|
|
351
|
+
nodeTypes: ['EachExpr', 'MapExpr', 'FoldExpr', 'FilterExpr'],
|
|
352
|
+
validate(node, context) {
|
|
353
|
+
const collectionNode = node;
|
|
354
|
+
const body = collectionNode.body;
|
|
355
|
+
if (isBlockWrappingMethod(body)) {
|
|
356
|
+
const methodName = getMethodName(body);
|
|
357
|
+
if (methodName) {
|
|
358
|
+
return [
|
|
359
|
+
{
|
|
360
|
+
location: node.span.start,
|
|
361
|
+
severity: 'info',
|
|
362
|
+
code: 'METHOD_SHORTHAND',
|
|
363
|
+
message: `Prefer method shorthand '.${methodName}' over block form '{ $.${methodName}() }'`,
|
|
364
|
+
context: extractContextLine(node.span.start.line, context.source),
|
|
365
|
+
fix: null, // Could generate fix replacing block with .method
|
|
366
|
+
},
|
|
367
|
+
];
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return [];
|
|
371
|
+
},
|
|
372
|
+
};
|
|
373
|
+
//# sourceMappingURL=collections.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collections.js","sourceRoot":"","sources":["../../../src/check/rules/collections.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conditional Convention Rules
|
|
3
|
+
* Enforces conventions for conditional expressions.
|
|
4
|
+
*/
|
|
5
|
+
import type { ValidationRule } from '../types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Suggests using ?? for defaults instead of verbose conditionals.
|
|
8
|
+
*
|
|
9
|
+
* The ?? operator is more concise for providing default values:
|
|
10
|
+
*
|
|
11
|
+
* Good (concise default):
|
|
12
|
+
* $dict.field ?? "default"
|
|
13
|
+
*
|
|
14
|
+
* Avoid (verbose conditional):
|
|
15
|
+
* $dict.?field ? $dict.field ! "default"
|
|
16
|
+
*
|
|
17
|
+
* This is informational - both patterns work identically.
|
|
18
|
+
*
|
|
19
|
+
* References:
|
|
20
|
+
* - docs/16_conventions.md:219-234
|
|
21
|
+
*/
|
|
22
|
+
export declare const USE_DEFAULT_OPERATOR: ValidationRule;
|
|
23
|
+
/**
|
|
24
|
+
* Validates that conditional conditions evaluate to boolean.
|
|
25
|
+
*
|
|
26
|
+
* Rill requires explicit boolean conditions in conditionals.
|
|
27
|
+
* The condition in `cond ? then ! else` must evaluate to boolean.
|
|
28
|
+
*
|
|
29
|
+
* Correct (boolean condition):
|
|
30
|
+
* "hello" -> .contains("ell") ? "found" ! "not found"
|
|
31
|
+
*
|
|
32
|
+
* Incorrect (non-boolean):
|
|
33
|
+
* "hello" ? "has value" ! "empty" # strings don't auto-convert to boolean
|
|
34
|
+
*
|
|
35
|
+
* This is a warning because it's likely a bug, not just stylistic.
|
|
36
|
+
*
|
|
37
|
+
* References:
|
|
38
|
+
* - docs/16_conventions.md:199-215
|
|
39
|
+
*/
|
|
40
|
+
export declare const CONDITION_TYPE: ValidationRule;
|
|
41
|
+
//# sourceMappingURL=conditionals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditionals.d.ts","sourceRoot":"","sources":["../../../src/check/rules/conditionals.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,aAAa,CAAC;AAiCrB;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,oBAAoB,EAAE,cA0BlC,CAAC;AAMF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc,EAAE,cAkB5B,CAAC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conditional Convention Rules
|
|
3
|
+
* Enforces conventions for conditional expressions.
|
|
4
|
+
*/
|
|
5
|
+
import { extractContextLine } from './helpers.js';
|
|
6
|
+
// ============================================================
|
|
7
|
+
// HELPER FUNCTIONS
|
|
8
|
+
// ============================================================
|
|
9
|
+
/**
|
|
10
|
+
* Check if a conditional is using the ?? pattern with .? check.
|
|
11
|
+
* Pattern: $dict.?field ? $dict.field ! "default"
|
|
12
|
+
* This should be simplified to: $dict.field ?? "default"
|
|
13
|
+
*/
|
|
14
|
+
function isVerboseDefaultPattern(node) {
|
|
15
|
+
// Check if condition is an existence check (.?field)
|
|
16
|
+
if (!node.condition)
|
|
17
|
+
return false;
|
|
18
|
+
// Simple heuristic: check if condition contains existence check
|
|
19
|
+
const conditionStr = JSON.stringify(node.condition);
|
|
20
|
+
if (!conditionStr.includes('"existenceCheck"')) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
// Check if there's an else branch (required for default pattern)
|
|
24
|
+
if (!node.elseBranch)
|
|
25
|
+
return false;
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
// ============================================================
|
|
29
|
+
// USE_DEFAULT_OPERATOR RULE
|
|
30
|
+
// ============================================================
|
|
31
|
+
/**
|
|
32
|
+
* Suggests using ?? for defaults instead of verbose conditionals.
|
|
33
|
+
*
|
|
34
|
+
* The ?? operator is more concise for providing default values:
|
|
35
|
+
*
|
|
36
|
+
* Good (concise default):
|
|
37
|
+
* $dict.field ?? "default"
|
|
38
|
+
*
|
|
39
|
+
* Avoid (verbose conditional):
|
|
40
|
+
* $dict.?field ? $dict.field ! "default"
|
|
41
|
+
*
|
|
42
|
+
* This is informational - both patterns work identically.
|
|
43
|
+
*
|
|
44
|
+
* References:
|
|
45
|
+
* - docs/16_conventions.md:219-234
|
|
46
|
+
*/
|
|
47
|
+
export const USE_DEFAULT_OPERATOR = {
|
|
48
|
+
code: 'USE_DEFAULT_OPERATOR',
|
|
49
|
+
category: 'conditionals',
|
|
50
|
+
severity: 'info',
|
|
51
|
+
nodeTypes: ['Conditional'],
|
|
52
|
+
validate(node, context) {
|
|
53
|
+
const conditional = node;
|
|
54
|
+
// Check for verbose default pattern
|
|
55
|
+
if (isVerboseDefaultPattern(conditional)) {
|
|
56
|
+
return [
|
|
57
|
+
{
|
|
58
|
+
location: node.span.start,
|
|
59
|
+
severity: 'info',
|
|
60
|
+
code: 'USE_DEFAULT_OPERATOR',
|
|
61
|
+
message: 'Use ?? for defaults instead of conditionals: $dict.field ?? "default"',
|
|
62
|
+
context: extractContextLine(node.span.start.line, context.source),
|
|
63
|
+
fix: null, // Complex fix - requires AST restructuring
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
return [];
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
// ============================================================
|
|
71
|
+
// CONDITION_TYPE RULE
|
|
72
|
+
// ============================================================
|
|
73
|
+
/**
|
|
74
|
+
* Validates that conditional conditions evaluate to boolean.
|
|
75
|
+
*
|
|
76
|
+
* Rill requires explicit boolean conditions in conditionals.
|
|
77
|
+
* The condition in `cond ? then ! else` must evaluate to boolean.
|
|
78
|
+
*
|
|
79
|
+
* Correct (boolean condition):
|
|
80
|
+
* "hello" -> .contains("ell") ? "found" ! "not found"
|
|
81
|
+
*
|
|
82
|
+
* Incorrect (non-boolean):
|
|
83
|
+
* "hello" ? "has value" ! "empty" # strings don't auto-convert to boolean
|
|
84
|
+
*
|
|
85
|
+
* This is a warning because it's likely a bug, not just stylistic.
|
|
86
|
+
*
|
|
87
|
+
* References:
|
|
88
|
+
* - docs/16_conventions.md:199-215
|
|
89
|
+
*/
|
|
90
|
+
export const CONDITION_TYPE = {
|
|
91
|
+
code: 'CONDITION_TYPE',
|
|
92
|
+
category: 'conditionals',
|
|
93
|
+
severity: 'warning',
|
|
94
|
+
nodeTypes: ['Conditional'],
|
|
95
|
+
validate(_node, _context) {
|
|
96
|
+
// Rill conditionals don't enforce boolean type checking at the static analysis level
|
|
97
|
+
// The language allows truthy/falsy semantics, and runtime will handle type errors
|
|
98
|
+
// This rule is disabled for now - the convention is informational only
|
|
99
|
+
// Note: If we wanted to enforce this, we would need to check:
|
|
100
|
+
// - When condition is null: input is the tested value (truthy check)
|
|
101
|
+
// - When condition exists: condition body must evaluate to boolean
|
|
102
|
+
// For now, return no diagnostics
|
|
103
|
+
return [];
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
//# sourceMappingURL=conditionals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditionals.js","sourceRoot":"","sources":["../../../src/check/rules/conditionals.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,+DAA+D;AAC/D,mBAAmB;AACnB,+DAA+D;AAE/D;;;;GAIG;AACH,SAAS,uBAAuB,CAAC,IAAqB;IACpD,qDAAqD;IACrD,IAAI,CAAC,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAElC,gEAAgE;IAChE,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACpD,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAC/C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,iEAAiE;IACjE,IAAI,CAAC,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAEnC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,+DAA+D;AAC/D,4BAA4B;AAC5B,+DAA+D;AAE/D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAmB;IAClD,IAAI,EAAE,sBAAsB;IAC5B,QAAQ,EAAE,cAAc;IACxB,QAAQ,EAAE,MAAM;IAChB,SAAS,EAAE,CAAC,aAAa,CAAC;IAE1B,QAAQ,CAAC,IAAa,EAAE,OAA0B;QAChD,MAAM,WAAW,GAAG,IAAuB,CAAC;QAE5C,oCAAoC;QACpC,IAAI,uBAAuB,CAAC,WAAW,CAAC,EAAE,CAAC;YACzC,OAAO;gBACL;oBACE,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK;oBACzB,QAAQ,EAAE,MAAM;oBAChB,IAAI,EAAE,sBAAsB;oBAC5B,OAAO,EACL,uEAAuE;oBACzE,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC;oBACjE,GAAG,EAAE,IAAI,EAAE,2CAA2C;iBACvD;aACF,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC;AAEF,+DAA+D;AAC/D,sBAAsB;AACtB,+DAA+D;AAE/D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAmB;IAC5C,IAAI,EAAE,gBAAgB;IACtB,QAAQ,EAAE,cAAc;IACxB,QAAQ,EAAE,SAAS;IACnB,SAAS,EAAE,CAAC,aAAa,CAAC;IAE1B,QAAQ,CAAC,KAAc,EAAE,QAA2B;QAClD,qFAAqF;QACrF,kFAAkF;QAClF,uEAAuE;QAEvE,8DAA8D;QAC9D,qEAAqE;QACrE,mEAAmE;QAEnE,iCAAiC;QACjC,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow and Capture Rules
|
|
3
|
+
* Enforces conventions for capture placement and flow patterns.
|
|
4
|
+
*/
|
|
5
|
+
import type { ValidationRule } from '../types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Validates that captures use inline syntax when continuing the chain.
|
|
8
|
+
*
|
|
9
|
+
* Detects separate capture followed by variable usage:
|
|
10
|
+
* prompt("Read file") :> $raw
|
|
11
|
+
* $raw -> log
|
|
12
|
+
*
|
|
13
|
+
* Suggests inline capture:
|
|
14
|
+
* prompt("Read file") :> $raw -> log
|
|
15
|
+
*
|
|
16
|
+
* This is an informational rule - both patterns work, but inline is clearer.
|
|
17
|
+
*
|
|
18
|
+
* References:
|
|
19
|
+
* - docs/16_conventions.md:56-74
|
|
20
|
+
*/
|
|
21
|
+
export declare const CAPTURE_INLINE_CHAIN: ValidationRule;
|
|
22
|
+
/**
|
|
23
|
+
* Validates that values used in multiple branches are captured before the conditional.
|
|
24
|
+
*
|
|
25
|
+
* Detects conditionals where a function call or expression appears in multiple branches:
|
|
26
|
+
* checkStatus() -> .contains("OK") ? {
|
|
27
|
+
* "Success: {checkStatus()}"
|
|
28
|
+
* } ! {
|
|
29
|
+
* "Failed: {checkStatus()}"
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* Suggests capturing before branching:
|
|
33
|
+
* checkStatus() :> $result
|
|
34
|
+
* $result -> .contains("OK") ? {
|
|
35
|
+
* "Success: {$result}"
|
|
36
|
+
* } ! {
|
|
37
|
+
* "Failed: {$result}"
|
|
38
|
+
* }
|
|
39
|
+
*
|
|
40
|
+
* This is an informational rule - detects potential inefficiency and clarity issues.
|
|
41
|
+
*
|
|
42
|
+
* References:
|
|
43
|
+
* - docs/16_conventions.md:76-88
|
|
44
|
+
*/
|
|
45
|
+
export declare const CAPTURE_BEFORE_BRANCH: ValidationRule;
|
|
46
|
+
//# sourceMappingURL=flow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../../../src/check/rules/flow.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,aAAa,CAAC;AAsErB;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB,EAAE,cA0ElC,CAAC;AAMF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAgEnC,CAAC"}
|