@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.
Files changed (260) hide show
  1. package/README.md +16 -8
  2. package/dist/check/config.d.ts +20 -0
  3. package/dist/check/config.d.ts.map +1 -0
  4. package/dist/check/config.js +151 -0
  5. package/dist/check/config.js.map +1 -0
  6. package/dist/check/fixer.d.ts +39 -0
  7. package/dist/check/fixer.d.ts.map +1 -0
  8. package/dist/check/fixer.js +119 -0
  9. package/dist/check/fixer.js.map +1 -0
  10. package/dist/check/index.d.ts +10 -0
  11. package/dist/check/index.d.ts.map +1 -0
  12. package/dist/check/index.js +21 -0
  13. package/dist/check/index.js.map +1 -0
  14. package/dist/check/rules/anti-patterns.d.ts +65 -0
  15. package/dist/check/rules/anti-patterns.d.ts.map +1 -0
  16. package/dist/check/rules/anti-patterns.js +427 -0
  17. package/dist/check/rules/anti-patterns.js.map +1 -0
  18. package/dist/check/rules/closures.d.ts +66 -0
  19. package/dist/check/rules/closures.d.ts.map +1 -0
  20. package/dist/check/rules/closures.js +373 -0
  21. package/dist/check/rules/closures.js.map +1 -0
  22. package/dist/check/rules/collections.d.ts +90 -0
  23. package/dist/check/rules/collections.d.ts.map +1 -0
  24. package/dist/check/rules/collections.js +373 -0
  25. package/dist/check/rules/collections.js.map +1 -0
  26. package/dist/check/rules/conditionals.d.ts +41 -0
  27. package/dist/check/rules/conditionals.d.ts.map +1 -0
  28. package/dist/check/rules/conditionals.js +106 -0
  29. package/dist/check/rules/conditionals.js.map +1 -0
  30. package/dist/check/rules/flow.d.ts +46 -0
  31. package/dist/check/rules/flow.d.ts.map +1 -0
  32. package/dist/check/rules/flow.js +206 -0
  33. package/dist/check/rules/flow.js.map +1 -0
  34. package/dist/check/rules/formatting.d.ts +133 -0
  35. package/dist/check/rules/formatting.d.ts.map +1 -0
  36. package/dist/check/rules/formatting.js +639 -0
  37. package/dist/check/rules/formatting.js.map +1 -0
  38. package/dist/check/rules/helpers.d.ts +26 -0
  39. package/dist/check/rules/helpers.d.ts.map +1 -0
  40. package/dist/check/rules/helpers.js +66 -0
  41. package/dist/check/rules/helpers.js.map +1 -0
  42. package/dist/check/rules/index.d.ts +21 -0
  43. package/dist/check/rules/index.d.ts.map +1 -0
  44. package/dist/check/rules/index.js +78 -0
  45. package/dist/check/rules/index.js.map +1 -0
  46. package/dist/check/rules/loops.d.ts +70 -0
  47. package/dist/check/rules/loops.d.ts.map +1 -0
  48. package/dist/check/rules/loops.js +227 -0
  49. package/dist/check/rules/loops.js.map +1 -0
  50. package/dist/check/rules/naming.d.ts +21 -0
  51. package/dist/check/rules/naming.d.ts.map +1 -0
  52. package/dist/check/rules/naming.js +167 -0
  53. package/dist/check/rules/naming.js.map +1 -0
  54. package/dist/check/rules/strings.d.ts +28 -0
  55. package/dist/check/rules/strings.d.ts.map +1 -0
  56. package/dist/check/rules/strings.js +80 -0
  57. package/dist/check/rules/strings.js.map +1 -0
  58. package/dist/check/rules/types.d.ts +41 -0
  59. package/dist/check/rules/types.d.ts.map +1 -0
  60. package/dist/check/rules/types.js +162 -0
  61. package/dist/check/rules/types.js.map +1 -0
  62. package/dist/check/types.d.ts +106 -0
  63. package/dist/check/types.d.ts.map +1 -0
  64. package/dist/check/types.js +6 -0
  65. package/dist/check/types.js.map +1 -0
  66. package/dist/check/validator.d.ts +18 -0
  67. package/dist/check/validator.d.ts.map +1 -0
  68. package/dist/check/validator.js +88 -0
  69. package/dist/check/validator.js.map +1 -0
  70. package/dist/check/visitor.d.ts +33 -0
  71. package/dist/check/visitor.d.ts.map +1 -0
  72. package/dist/check/visitor.js +243 -0
  73. package/dist/check/visitor.js.map +1 -0
  74. package/dist/cli-check.d.ts +43 -0
  75. package/dist/cli-check.d.ts.map +1 -0
  76. package/dist/cli-check.js +356 -0
  77. package/dist/cli-check.js.map +1 -0
  78. package/dist/cli-eval.d.ts +15 -0
  79. package/dist/cli-eval.d.ts.map +1 -0
  80. package/dist/cli-eval.js +120 -0
  81. package/dist/cli-eval.js.map +1 -0
  82. package/dist/cli-exec.d.ts +49 -0
  83. package/dist/cli-exec.d.ts.map +1 -0
  84. package/dist/cli-exec.js +191 -0
  85. package/dist/cli-exec.js.map +1 -0
  86. package/dist/cli-module-loader.d.ts +19 -0
  87. package/dist/cli-module-loader.d.ts.map +1 -0
  88. package/dist/cli-module-loader.js +83 -0
  89. package/dist/cli-module-loader.js.map +1 -0
  90. package/dist/cli-shared.d.ts +36 -0
  91. package/dist/cli-shared.d.ts.map +1 -0
  92. package/dist/cli-shared.js +101 -0
  93. package/dist/cli-shared.js.map +1 -0
  94. package/dist/cli.d.ts +2 -0
  95. package/dist/cli.d.ts.map +1 -1
  96. package/dist/cli.js +4 -11
  97. package/dist/cli.js.map +1 -1
  98. package/dist/index.d.ts +1 -1
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +1 -1
  101. package/dist/index.js.map +1 -1
  102. package/dist/lexer/readers.d.ts +1 -1
  103. package/dist/lexer/readers.d.ts.map +1 -1
  104. package/dist/lexer/readers.js +62 -32
  105. package/dist/lexer/readers.js.map +1 -1
  106. package/dist/lexer/tokenizer.d.ts.map +1 -1
  107. package/dist/lexer/tokenizer.js +5 -6
  108. package/dist/lexer/tokenizer.js.map +1 -1
  109. package/dist/parser/index.js +1 -1
  110. package/dist/parser/index.js.map +1 -1
  111. package/dist/parser/parser-expr.js +23 -5
  112. package/dist/parser/parser-expr.js.map +1 -1
  113. package/dist/parser/parser-functions.d.ts +2 -2
  114. package/dist/parser/parser-functions.d.ts.map +1 -1
  115. package/dist/parser/parser-functions.js +2 -1
  116. package/dist/parser/parser-functions.js.map +1 -1
  117. package/dist/parser/parser-literals.js +2 -2
  118. package/dist/parser/parser-literals.js.map +1 -1
  119. package/dist/parser/parser-script.js +9 -7
  120. package/dist/parser/parser-script.js.map +1 -1
  121. package/dist/parser/parser-variables.js +4 -3
  122. package/dist/parser/parser-variables.js.map +1 -1
  123. package/dist/runtime/core/callable.d.ts +5 -6
  124. package/dist/runtime/core/callable.d.ts.map +1 -1
  125. package/dist/runtime/core/callable.js.map +1 -1
  126. package/dist/runtime/core/context.d.ts.map +1 -1
  127. package/dist/runtime/core/context.js +19 -32
  128. package/dist/runtime/core/context.js.map +1 -1
  129. package/dist/runtime/core/equals.js +1 -1
  130. package/dist/runtime/core/equals.js.map +1 -1
  131. package/dist/runtime/core/eval/evaluator.d.ts +78 -0
  132. package/dist/runtime/core/eval/evaluator.d.ts.map +1 -1
  133. package/dist/runtime/core/eval/evaluator.js +78 -0
  134. package/dist/runtime/core/eval/evaluator.js.map +1 -1
  135. package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -1
  136. package/dist/runtime/core/eval/mixins/closures.js +9 -1
  137. package/dist/runtime/core/eval/mixins/closures.js.map +1 -1
  138. package/dist/runtime/core/eval/mixins/variables.d.ts.map +1 -1
  139. package/dist/runtime/core/eval/mixins/variables.js +143 -2
  140. package/dist/runtime/core/eval/mixins/variables.js.map +1 -1
  141. package/dist/runtime/core/types.d.ts +15 -2
  142. package/dist/runtime/core/types.d.ts.map +1 -1
  143. package/dist/runtime/core/types.js.map +1 -1
  144. package/dist/runtime/ext/extensions.d.ts +51 -0
  145. package/dist/runtime/ext/extensions.d.ts.map +1 -0
  146. package/dist/runtime/ext/extensions.js +67 -0
  147. package/dist/runtime/ext/extensions.js.map +1 -0
  148. package/dist/runtime/index.d.ts +3 -0
  149. package/dist/runtime/index.d.ts.map +1 -1
  150. package/dist/runtime/index.js +1 -0
  151. package/dist/runtime/index.js.map +1 -1
  152. package/dist/types.d.ts +8 -4
  153. package/dist/types.d.ts.map +1 -1
  154. package/dist/types.js +5 -4
  155. package/dist/types.js.map +1 -1
  156. package/docs/00_INDEX.md +1 -0
  157. package/docs/01_guide.md +3 -3
  158. package/docs/02_types.md +8 -10
  159. package/docs/03_variables.md +10 -0
  160. package/docs/04_operators.md +3 -3
  161. package/docs/05_control-flow.md +21 -0
  162. package/docs/07_collections.md +2 -0
  163. package/docs/10_parsing.md +9 -9
  164. package/docs/11_reference.md +1 -1
  165. package/docs/12_examples.md +36 -62
  166. package/docs/14_host-integration.md +116 -111
  167. package/docs/15_grammar.ebnf +1 -5
  168. package/docs/16_conventions.md +3 -4
  169. package/docs/17_cli-tools.md +184 -0
  170. package/docs/99_llm-reference.txt +46 -5
  171. package/package.json +13 -4
  172. package/dist/demo.d.ts +0 -6
  173. package/dist/demo.d.ts.map +0 -1
  174. package/dist/demo.js +0 -121
  175. package/dist/demo.js.map +0 -1
  176. package/dist/lexer.d.ts +0 -19
  177. package/dist/lexer.d.ts.map +0 -1
  178. package/dist/lexer.js +0 -344
  179. package/dist/lexer.js.map +0 -1
  180. package/dist/parser/arithmetic.d.ts +0 -16
  181. package/dist/parser/arithmetic.d.ts.map +0 -1
  182. package/dist/parser/arithmetic.js +0 -128
  183. package/dist/parser/arithmetic.js.map +0 -1
  184. package/dist/parser/boolean.d.ts +0 -15
  185. package/dist/parser/boolean.d.ts.map +0 -1
  186. package/dist/parser/boolean.js +0 -20
  187. package/dist/parser/boolean.js.map +0 -1
  188. package/dist/parser/control-flow.d.ts +0 -56
  189. package/dist/parser/control-flow.d.ts.map +0 -1
  190. package/dist/parser/control-flow.js +0 -167
  191. package/dist/parser/control-flow.js.map +0 -1
  192. package/dist/parser/expressions.d.ts +0 -23
  193. package/dist/parser/expressions.d.ts.map +0 -1
  194. package/dist/parser/expressions.js +0 -950
  195. package/dist/parser/expressions.js.map +0 -1
  196. package/dist/parser/extraction.d.ts +0 -48
  197. package/dist/parser/extraction.d.ts.map +0 -1
  198. package/dist/parser/extraction.js +0 -279
  199. package/dist/parser/extraction.js.map +0 -1
  200. package/dist/parser/functions.d.ts +0 -20
  201. package/dist/parser/functions.d.ts.map +0 -1
  202. package/dist/parser/functions.js +0 -96
  203. package/dist/parser/functions.js.map +0 -1
  204. package/dist/parser/literals.d.ts +0 -37
  205. package/dist/parser/literals.d.ts.map +0 -1
  206. package/dist/parser/literals.js +0 -373
  207. package/dist/parser/literals.js.map +0 -1
  208. package/dist/parser/script.d.ts +0 -14
  209. package/dist/parser/script.d.ts.map +0 -1
  210. package/dist/parser/script.js +0 -196
  211. package/dist/parser/script.js.map +0 -1
  212. package/dist/parser/variables.d.ts +0 -10
  213. package/dist/parser/variables.d.ts.map +0 -1
  214. package/dist/parser/variables.js +0 -215
  215. package/dist/parser/variables.js.map +0 -1
  216. package/dist/runtime/ast-equals.d.ts +0 -13
  217. package/dist/runtime/ast-equals.d.ts.map +0 -1
  218. package/dist/runtime/ast-equals.js +0 -447
  219. package/dist/runtime/ast-equals.js.map +0 -1
  220. package/dist/runtime/builtins.d.ts +0 -13
  221. package/dist/runtime/builtins.d.ts.map +0 -1
  222. package/dist/runtime/builtins.js +0 -180
  223. package/dist/runtime/builtins.js.map +0 -1
  224. package/dist/runtime/callable.d.ts +0 -88
  225. package/dist/runtime/callable.d.ts.map +0 -1
  226. package/dist/runtime/callable.js +0 -98
  227. package/dist/runtime/callable.js.map +0 -1
  228. package/dist/runtime/context.d.ts +0 -13
  229. package/dist/runtime/context.d.ts.map +0 -1
  230. package/dist/runtime/context.js +0 -73
  231. package/dist/runtime/context.js.map +0 -1
  232. package/dist/runtime/core/evaluate.d.ts +0 -42
  233. package/dist/runtime/core/evaluate.d.ts.map +0 -1
  234. package/dist/runtime/core/evaluate.debug.js +0 -1251
  235. package/dist/runtime/core/evaluate.js +0 -1913
  236. package/dist/runtime/core/evaluate.js.map +0 -1
  237. package/dist/runtime/evaluate.d.ts +0 -32
  238. package/dist/runtime/evaluate.d.ts.map +0 -1
  239. package/dist/runtime/evaluate.js +0 -1111
  240. package/dist/runtime/evaluate.js.map +0 -1
  241. package/dist/runtime/execute.d.ts +0 -26
  242. package/dist/runtime/execute.d.ts.map +0 -1
  243. package/dist/runtime/execute.js +0 -121
  244. package/dist/runtime/execute.js.map +0 -1
  245. package/dist/runtime/signals.d.ts +0 -19
  246. package/dist/runtime/signals.d.ts.map +0 -1
  247. package/dist/runtime/signals.js +0 -26
  248. package/dist/runtime/signals.js.map +0 -1
  249. package/dist/runtime/types.d.ts +0 -169
  250. package/dist/runtime/types.d.ts.map +0 -1
  251. package/dist/runtime/types.js +0 -50
  252. package/dist/runtime/types.js.map +0 -1
  253. package/dist/runtime/values.d.ts +0 -50
  254. package/dist/runtime/values.d.ts.map +0 -1
  255. package/dist/runtime/values.js +0 -209
  256. package/dist/runtime/values.js.map +0 -1
  257. package/dist/runtime.d.ts +0 -254
  258. package/dist/runtime.d.ts.map +0 -1
  259. package/dist/runtime.js +0 -2014
  260. 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"}