@rcrsr/rill 0.8.5 → 0.9.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 (190) hide show
  1. package/dist/ast-nodes.d.ts +189 -49
  2. package/dist/ast-nodes.d.ts.map +1 -1
  3. package/dist/ast-unions.d.ts +1 -1
  4. package/dist/ast-unions.d.ts.map +1 -1
  5. package/dist/constants.d.ts +14 -0
  6. package/dist/constants.d.ts.map +1 -0
  7. package/dist/constants.js +29 -0
  8. package/dist/constants.js.map +1 -0
  9. package/dist/error-classes.d.ts +3 -1
  10. package/dist/error-classes.d.ts.map +1 -1
  11. package/dist/error-classes.js +11 -5
  12. package/dist/error-classes.js.map +1 -1
  13. package/dist/error-registry.d.ts.map +1 -1
  14. package/dist/error-registry.js +293 -9
  15. package/dist/error-registry.js.map +1 -1
  16. package/dist/ext/crypto/index.d.ts +2 -1
  17. package/dist/ext/crypto/index.d.ts.map +1 -1
  18. package/dist/ext/crypto/index.js +7 -0
  19. package/dist/ext/crypto/index.js.map +1 -1
  20. package/dist/ext/exec/index.d.ts +2 -1
  21. package/dist/ext/exec/index.d.ts.map +1 -1
  22. package/dist/ext/exec/index.js +6 -0
  23. package/dist/ext/exec/index.js.map +1 -1
  24. package/dist/ext/fetch/index.d.ts +2 -1
  25. package/dist/ext/fetch/index.d.ts.map +1 -1
  26. package/dist/ext/fetch/index.js +6 -0
  27. package/dist/ext/fetch/index.js.map +1 -1
  28. package/dist/ext/fs/index.d.ts +2 -1
  29. package/dist/ext/fs/index.d.ts.map +1 -1
  30. package/dist/ext/fs/index.js +3 -0
  31. package/dist/ext/fs/index.js.map +1 -1
  32. package/dist/ext/kv/index.d.ts +48 -12
  33. package/dist/ext/kv/index.d.ts.map +1 -1
  34. package/dist/ext/kv/index.js +225 -63
  35. package/dist/ext/kv/index.js.map +1 -1
  36. package/dist/ext/kv/store.d.ts +4 -0
  37. package/dist/ext/kv/store.d.ts.map +1 -1
  38. package/dist/ext/kv/store.js +14 -0
  39. package/dist/ext/kv/store.js.map +1 -1
  40. package/dist/generated/introspection-data.d.ts +1 -1
  41. package/dist/generated/introspection-data.d.ts.map +1 -1
  42. package/dist/generated/introspection-data.js +194 -185
  43. package/dist/generated/introspection-data.js.map +1 -1
  44. package/dist/generated/version-data.d.ts +1 -1
  45. package/dist/generated/version-data.js +3 -3
  46. package/dist/highlight-map.d.ts.map +1 -1
  47. package/dist/highlight-map.js +8 -2
  48. package/dist/highlight-map.js.map +1 -1
  49. package/dist/index.d.ts +2 -1
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +5 -1
  52. package/dist/index.js.map +1 -1
  53. package/dist/lexer/operators.d.ts.map +1 -1
  54. package/dist/lexer/operators.js +0 -2
  55. package/dist/lexer/operators.js.map +1 -1
  56. package/dist/lexer/readers.d.ts +18 -1
  57. package/dist/lexer/readers.d.ts.map +1 -1
  58. package/dist/lexer/readers.js +55 -0
  59. package/dist/lexer/readers.js.map +1 -1
  60. package/dist/parser/helpers.d.ts +8 -13
  61. package/dist/parser/helpers.d.ts.map +1 -1
  62. package/dist/parser/helpers.js +42 -35
  63. package/dist/parser/helpers.js.map +1 -1
  64. package/dist/parser/index.d.ts +1 -0
  65. package/dist/parser/index.d.ts.map +1 -1
  66. package/dist/parser/index.js +1 -0
  67. package/dist/parser/index.js.map +1 -1
  68. package/dist/parser/parser-collect.d.ts.map +1 -1
  69. package/dist/parser/parser-collect.js +34 -5
  70. package/dist/parser/parser-collect.js.map +1 -1
  71. package/dist/parser/parser-control.d.ts.map +1 -1
  72. package/dist/parser/parser-control.js +9 -0
  73. package/dist/parser/parser-control.js.map +1 -1
  74. package/dist/parser/parser-expr.d.ts +3 -1
  75. package/dist/parser/parser-expr.d.ts.map +1 -1
  76. package/dist/parser/parser-expr.js +377 -100
  77. package/dist/parser/parser-expr.js.map +1 -1
  78. package/dist/parser/parser-extract.d.ts +3 -5
  79. package/dist/parser/parser-extract.d.ts.map +1 -1
  80. package/dist/parser/parser-extract.js +37 -69
  81. package/dist/parser/parser-extract.js.map +1 -1
  82. package/dist/parser/parser-functions.d.ts +2 -2
  83. package/dist/parser/parser-functions.d.ts.map +1 -1
  84. package/dist/parser/parser-functions.js +112 -36
  85. package/dist/parser/parser-functions.js.map +1 -1
  86. package/dist/parser/parser-literals.d.ts +4 -3
  87. package/dist/parser/parser-literals.d.ts.map +1 -1
  88. package/dist/parser/parser-literals.js +257 -42
  89. package/dist/parser/parser-literals.js.map +1 -1
  90. package/dist/parser/parser-script.d.ts.map +1 -1
  91. package/dist/parser/parser-script.js +25 -12
  92. package/dist/parser/parser-script.js.map +1 -1
  93. package/dist/parser/parser-shape.d.ts +13 -0
  94. package/dist/parser/parser-shape.d.ts.map +1 -0
  95. package/dist/parser/parser-shape.js +72 -0
  96. package/dist/parser/parser-shape.js.map +1 -0
  97. package/dist/parser/parser-types.d.ts +21 -0
  98. package/dist/parser/parser-types.d.ts.map +1 -0
  99. package/dist/parser/parser-types.js +30 -0
  100. package/dist/parser/parser-types.js.map +1 -0
  101. package/dist/parser/parser-variables.d.ts.map +1 -1
  102. package/dist/parser/parser-variables.js +10 -1
  103. package/dist/parser/parser-variables.js.map +1 -1
  104. package/dist/runtime/core/callable.d.ts +25 -22
  105. package/dist/runtime/core/callable.d.ts.map +1 -1
  106. package/dist/runtime/core/callable.js +43 -36
  107. package/dist/runtime/core/callable.js.map +1 -1
  108. package/dist/runtime/core/context.d.ts.map +1 -1
  109. package/dist/runtime/core/context.js +8 -8
  110. package/dist/runtime/core/context.js.map +1 -1
  111. package/dist/runtime/core/equals.d.ts.map +1 -1
  112. package/dist/runtime/core/equals.js +142 -30
  113. package/dist/runtime/core/equals.js.map +1 -1
  114. package/dist/runtime/core/eval/base.d.ts +2 -2
  115. package/dist/runtime/core/eval/base.d.ts.map +1 -1
  116. package/dist/runtime/core/eval/evaluator.d.ts.map +1 -1
  117. package/dist/runtime/core/eval/evaluator.js +3 -1
  118. package/dist/runtime/core/eval/evaluator.js.map +1 -1
  119. package/dist/runtime/core/eval/index.d.ts +16 -1
  120. package/dist/runtime/core/eval/index.d.ts.map +1 -1
  121. package/dist/runtime/core/eval/index.js +22 -2
  122. package/dist/runtime/core/eval/index.js.map +1 -1
  123. package/dist/runtime/core/eval/mixins/annotations.d.ts.map +1 -1
  124. package/dist/runtime/core/eval/mixins/annotations.js +14 -8
  125. package/dist/runtime/core/eval/mixins/annotations.js.map +1 -1
  126. package/dist/runtime/core/eval/mixins/closures.d.ts +0 -2
  127. package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -1
  128. package/dist/runtime/core/eval/mixins/closures.js +286 -105
  129. package/dist/runtime/core/eval/mixins/closures.js.map +1 -1
  130. package/dist/runtime/core/eval/mixins/collections.d.ts.map +1 -1
  131. package/dist/runtime/core/eval/mixins/collections.js +65 -25
  132. package/dist/runtime/core/eval/mixins/collections.js.map +1 -1
  133. package/dist/runtime/core/eval/mixins/control-flow.d.ts.map +1 -1
  134. package/dist/runtime/core/eval/mixins/control-flow.js +21 -17
  135. package/dist/runtime/core/eval/mixins/control-flow.js.map +1 -1
  136. package/dist/runtime/core/eval/mixins/conversion.d.ts +30 -0
  137. package/dist/runtime/core/eval/mixins/conversion.d.ts.map +1 -0
  138. package/dist/runtime/core/eval/mixins/conversion.js +203 -0
  139. package/dist/runtime/core/eval/mixins/conversion.js.map +1 -0
  140. package/dist/runtime/core/eval/mixins/core.d.ts.map +1 -1
  141. package/dist/runtime/core/eval/mixins/core.js +101 -32
  142. package/dist/runtime/core/eval/mixins/core.js.map +1 -1
  143. package/dist/runtime/core/eval/mixins/extraction.d.ts.map +1 -1
  144. package/dist/runtime/core/eval/mixins/extraction.js +136 -30
  145. package/dist/runtime/core/eval/mixins/extraction.js.map +1 -1
  146. package/dist/runtime/core/eval/mixins/list-dispatch.d.ts +17 -0
  147. package/dist/runtime/core/eval/mixins/list-dispatch.d.ts.map +1 -0
  148. package/dist/runtime/core/eval/mixins/list-dispatch.js +97 -0
  149. package/dist/runtime/core/eval/mixins/list-dispatch.js.map +1 -0
  150. package/dist/runtime/core/eval/mixins/literals.d.ts.map +1 -1
  151. package/dist/runtime/core/eval/mixins/literals.js +64 -82
  152. package/dist/runtime/core/eval/mixins/literals.js.map +1 -1
  153. package/dist/runtime/core/eval/mixins/types.d.ts +4 -0
  154. package/dist/runtime/core/eval/mixins/types.d.ts.map +1 -1
  155. package/dist/runtime/core/eval/mixins/types.js +202 -3
  156. package/dist/runtime/core/eval/mixins/types.js.map +1 -1
  157. package/dist/runtime/core/eval/mixins/variables.d.ts.map +1 -1
  158. package/dist/runtime/core/eval/mixins/variables.js +42 -7
  159. package/dist/runtime/core/eval/mixins/variables.js.map +1 -1
  160. package/dist/runtime/core/execute.d.ts.map +1 -1
  161. package/dist/runtime/core/execute.js +2 -15
  162. package/dist/runtime/core/execute.js.map +1 -1
  163. package/dist/runtime/core/field-descriptor.d.ts +29 -0
  164. package/dist/runtime/core/field-descriptor.d.ts.map +1 -0
  165. package/dist/runtime/core/field-descriptor.js +27 -0
  166. package/dist/runtime/core/field-descriptor.js.map +1 -0
  167. package/dist/runtime/core/types.d.ts +15 -6
  168. package/dist/runtime/core/types.d.ts.map +1 -1
  169. package/dist/runtime/core/types.js.map +1 -1
  170. package/dist/runtime/core/values.d.ts +107 -9
  171. package/dist/runtime/core/values.d.ts.map +1 -1
  172. package/dist/runtime/core/values.js +453 -43
  173. package/dist/runtime/core/values.js.map +1 -1
  174. package/dist/runtime/ext/builtins.d.ts.map +1 -1
  175. package/dist/runtime/ext/builtins.js +47 -107
  176. package/dist/runtime/ext/builtins.js.map +1 -1
  177. package/dist/runtime/ext/extensions.d.ts +134 -1
  178. package/dist/runtime/ext/extensions.d.ts.map +1 -1
  179. package/dist/runtime/ext/extensions.js.map +1 -1
  180. package/dist/runtime/index.d.ts +7 -4
  181. package/dist/runtime/index.d.ts.map +1 -1
  182. package/dist/runtime/index.js +7 -2
  183. package/dist/runtime/index.js.map +1 -1
  184. package/dist/token-types.d.ts +7 -2
  185. package/dist/token-types.d.ts.map +1 -1
  186. package/dist/token-types.js +9 -2
  187. package/dist/token-types.js.map +1 -1
  188. package/dist/value-types.d.ts +19 -1
  189. package/dist/value-types.d.ts.map +1 -1
  190. package/package.json +1 -1
@@ -5,7 +5,8 @@
5
5
  import { Parser } from './parser.js';
6
6
  import { ParseError, TOKEN_TYPES } from '../types.js';
7
7
  import { check, advance, expect, current, makeSpan, peek, skipNewlines, skipNewlinesIfFollowedBy, } from './state.js';
8
- import { isHostCall, isClosureCall, isClosureCallWithAccess, canStartPipeInvoke, isMethodCall, isClosureChainTarget, isNegativeNumber, isLiteralStart, isClosureStart, makeBoolLiteralBlock, parseBareHostCall, isDictStart, VALID_TYPE_NAMES, parseTypeName, } from './helpers.js';
8
+ import { isHostCall, isClosureCall, isClosureCallWithAccess, canStartPipeInvoke, isAnnotationAccess, isMethodCall, isNegativeNumber, isLiteralStart, isClosureStart, makeBoolLiteralBlock, parseBareHostCall, VALID_TYPE_NAMES, } from './helpers.js';
9
+ import { parseTypeRef } from './parser-types.js';
9
10
  // ============================================================
10
11
  // COMMON CONSTRUCT PARSER
11
12
  // ============================================================
@@ -57,7 +58,13 @@ Parser.prototype.parseCommonConstruct = function () {
57
58
  return this.parsePipedConditional();
58
59
  }
59
60
  // Loop: @ body [? cond]
61
+ // Guard: @[ and @$fn are not valid expression forms (RILL-P010)
62
+ // @ is only valid as a do-while terminator inside a loop body.
60
63
  if (check(this.state, TOKEN_TYPES.AT)) {
64
+ const nextType = peek(this.state, 1).type;
65
+ if (nextType === TOKEN_TYPES.LBRACKET || nextType === TOKEN_TYPES.DOLLAR) {
66
+ throw new ParseError('RILL-P010', `'@${nextType === TOKEN_TYPES.LBRACKET ? '[' : '$'}...' is not a valid expression; use chain(...) to chain collections`, current(this.state).span.start);
67
+ }
61
68
  return this.parseLoop(null);
62
69
  }
63
70
  // Block (may be followed by @ for loop with input, or ? for conditional)
@@ -165,13 +172,6 @@ Parser.prototype.parsePipeChain = function () {
165
172
  let terminator = null;
166
173
  // Helper: check for -> or => possibly after newlines (line continuation)
167
174
  const checkChainContinuation = () => {
168
- // Detect deprecated :> syntax (COLON followed by GT)
169
- if (check(this.state, TOKEN_TYPES.COLON)) {
170
- const nextToken = peek(this.state, 1);
171
- if (nextToken.type === TOKEN_TYPES.GT) {
172
- throw new ParseError('RILL-P006', 'The capture arrow syntax changed from :> to =>', current(this.state).span.start);
173
- }
174
- }
175
175
  if (check(this.state, TOKEN_TYPES.ARROW) ||
176
176
  check(this.state, TOKEN_TYPES.CAPTURE_ARROW)) {
177
177
  return true;
@@ -183,16 +183,6 @@ Parser.prototype.parsePipeChain = function () {
183
183
  lookahead++;
184
184
  }
185
185
  const nextToken = peek(this.state, lookahead);
186
- // Detect deprecated :> after newlines
187
- if (nextToken.type === TOKEN_TYPES.COLON) {
188
- const tokenAfterColon = peek(this.state, lookahead + 1);
189
- if (tokenAfterColon.type === TOKEN_TYPES.GT) {
190
- // Skip newlines to reach the colon for accurate error location
191
- while (check(this.state, TOKEN_TYPES.NEWLINE))
192
- advance(this.state);
193
- throw new ParseError('RILL-P006', 'The capture arrow syntax changed from :> to =>', current(this.state).span.start);
194
- }
195
- }
196
186
  if (nextToken.type === TOKEN_TYPES.ARROW ||
197
187
  nextToken.type === TOKEN_TYPES.CAPTURE_ARROW) {
198
188
  // Skip newlines to reach the arrow
@@ -273,12 +263,7 @@ Parser.prototype.parsePostfixExprBase = function () {
273
263
  const start = current(this.state).span.start;
274
264
  let primary = this.parsePrimary();
275
265
  // Check for postfix type assertion: expr:type or expr:?type
276
- if (check(this.state, TOKEN_TYPES.COLON)) {
277
- // Detect deprecated :> syntax before type operation
278
- const nextToken = peek(this.state, 1);
279
- if (nextToken.type === TOKEN_TYPES.GT) {
280
- throw new ParseError('RILL-P006', 'The capture arrow syntax changed from :> to =>', current(this.state).span.start);
281
- }
266
+ if (skipNewlinesIfFollowedBy(this.state, TOKEN_TYPES.COLON)) {
282
267
  primary = this.parsePostfixTypeOperation(primary, start);
283
268
  }
284
269
  const methods = [];
@@ -293,8 +278,23 @@ Parser.prototype.parsePostfixExprBase = function () {
293
278
  (primary.thenBranch?.type === 'PipeChain' &&
294
279
  primary.thenBranch.terminator !== null));
295
280
  while (!shouldStopPostfix &&
296
- (isMethodCall(this.state) || check(this.state, TOKEN_TYPES.LPAREN))) {
297
- if (isMethodCall(this.state)) {
281
+ (isAnnotationAccess(this.state) ||
282
+ isMethodCall(this.state) ||
283
+ check(this.state, TOKEN_TYPES.LPAREN))) {
284
+ if (isAnnotationAccess(this.state)) {
285
+ const dotStart = current(this.state).span.start;
286
+ advance(this.state); // consume .
287
+ advance(this.state); // consume ^
288
+ const nameToken = expect(this.state, TOKEN_TYPES.IDENTIFIER, 'Expected annotation key after .^');
289
+ const annotationAccess = {
290
+ type: 'AnnotationAccess',
291
+ key: nameToken.value,
292
+ span: makeSpan(dotStart, nameToken.span.end),
293
+ };
294
+ methods.push(annotationAccess);
295
+ receiverEnd = nameToken.span.end;
296
+ }
297
+ else if (isMethodCall(this.state)) {
298
298
  // Capture receiver span: from start to current receiver end
299
299
  const receiverSpan = makeSpan(start, receiverEnd);
300
300
  const method = this.parseMethodCall(receiverSpan);
@@ -328,14 +328,22 @@ Parser.prototype.parsePostfixExprBase = function () {
328
328
  Parser.prototype.parseInvoke = function () {
329
329
  const start = current(this.state).span.start;
330
330
  expect(this.state, TOKEN_TYPES.LPAREN, 'Expected (');
331
+ skipNewlines(this.state);
331
332
  const args = [];
333
+ let hasSpread = false;
332
334
  if (!check(this.state, TOKEN_TYPES.RPAREN)) {
333
- args.push(this.parsePipeChain());
335
+ args.push(parseInvokeArg(this, hasSpread));
336
+ if (args[args.length - 1].type === 'SpreadArg')
337
+ hasSpread = true;
334
338
  while (check(this.state, TOKEN_TYPES.COMMA)) {
335
339
  advance(this.state);
336
- args.push(this.parsePipeChain());
340
+ skipNewlines(this.state);
341
+ args.push(parseInvokeArg(this, hasSpread));
342
+ if (args[args.length - 1].type === 'SpreadArg')
343
+ hasSpread = true;
337
344
  }
338
345
  }
346
+ skipNewlines(this.state);
339
347
  const rparen = expect(this.state, TOKEN_TYPES.RPAREN, 'Expected )', 'RILL-P005');
340
348
  return {
341
349
  type: 'Invoke',
@@ -343,10 +351,119 @@ Parser.prototype.parseInvoke = function () {
343
351
  span: makeSpan(start, rparen.span.end),
344
352
  };
345
353
  };
354
+ /**
355
+ * Parse one argument inside parseInvoke, with spread support.
356
+ * Bare `...` synthesizes VariableNode for `$`. Max one spread per list.
357
+ */
358
+ function parseInvokeArg(parser, hasSpread) {
359
+ if (check(parser.state, TOKEN_TYPES.ELLIPSIS)) {
360
+ if (hasSpread) {
361
+ throw new ParseError('RILL-P007', 'Only one spread argument (...) is allowed per argument list', current(parser.state).span.start);
362
+ }
363
+ const start = current(parser.state).span.start;
364
+ advance(parser.state); // consume ...
365
+ // Bare `...` before `)` or `,` → synthesize VariableNode for `$`
366
+ if (check(parser.state, TOKEN_TYPES.RPAREN) ||
367
+ check(parser.state, TOKEN_TYPES.COMMA)) {
368
+ const spreadSpan = makeSpan(start, current(parser.state).span.start);
369
+ const varNode = {
370
+ type: 'Variable',
371
+ name: null,
372
+ isPipeVar: true,
373
+ accessChain: [],
374
+ defaultValue: null,
375
+ existenceCheck: null,
376
+ span: spreadSpan,
377
+ };
378
+ const postfixNode = {
379
+ type: 'PostfixExpr',
380
+ primary: varNode,
381
+ methods: [],
382
+ defaultValue: null,
383
+ span: spreadSpan,
384
+ };
385
+ const pipeChainNode = {
386
+ type: 'PipeChain',
387
+ head: postfixNode,
388
+ pipes: [],
389
+ terminator: null,
390
+ span: spreadSpan,
391
+ };
392
+ return {
393
+ type: 'SpreadArg',
394
+ expression: pipeChainNode,
395
+ span: spreadSpan,
396
+ };
397
+ }
398
+ const expression = parser.parsePipeChain();
399
+ return {
400
+ type: 'SpreadArg',
401
+ expression,
402
+ span: makeSpan(start, current(parser.state).span.end),
403
+ };
404
+ }
405
+ return parser.parsePipeChain();
406
+ }
407
+ // ============================================================
408
+ // CLOSURE SIG LITERAL HELPERS
409
+ // ============================================================
410
+ /**
411
+ * Lookahead: PIPE_BAR ... PIPE_BAR COLON → closure sig literal.
412
+ * A closure literal has `| param |` followed by a body (`{` or expression).
413
+ * A closure sig literal has `| name: typeExpr, ... | :returnType`.
414
+ * The distinguishing pattern is PIPE_BAR at pos+0, IDENTIFIER at pos+1, COLON at pos+2,
415
+ * AND the matching closing PIPE_BAR is followed by COLON (:).
416
+ * This avoids misidentifying typed closures |x: T| { body } as sig literals
417
+ * because those have `{` after the closing `|`, not `:`.
418
+ */
419
+ function isClosureSigLiteralStart(state) {
420
+ const t0 = state.tokens[state.pos];
421
+ const t1 = state.tokens[state.pos + 1];
422
+ const t2 = state.tokens[state.pos + 2];
423
+ if (!t0 || !t1 || !t2)
424
+ return false;
425
+ if (t0.type !== TOKEN_TYPES.PIPE_BAR ||
426
+ t1.type !== TOKEN_TYPES.IDENTIFIER ||
427
+ t2.type !== TOKEN_TYPES.COLON) {
428
+ return false;
429
+ }
430
+ // Scan forward to find the matching closing PIPE_BAR, then check for COLON.
431
+ // Track nested pipe bars (|| is OR, not PIPE_BAR so we only count PIPE_BAR).
432
+ let depth = 1;
433
+ let i = state.pos + 1;
434
+ while (i < state.tokens.length) {
435
+ const tok = state.tokens[i];
436
+ if (tok.type === TOKEN_TYPES.PIPE_BAR) {
437
+ depth -= 1;
438
+ if (depth === 0) {
439
+ const afterClose = state.tokens[i + 1];
440
+ return afterClose?.type === TOKEN_TYPES.COLON;
441
+ }
442
+ }
443
+ i += 1;
444
+ }
445
+ return false;
446
+ }
346
447
  // ============================================================
347
448
  // PRIMARY PARSING
348
449
  // ============================================================
349
450
  Parser.prototype.parsePrimary = function () {
451
+ // Expression-position annotation: ^(...) expression (IR-5)
452
+ if (check(this.state, TOKEN_TYPES.CARET) &&
453
+ peek(this.state, 1).type === TOKEN_TYPES.LPAREN) {
454
+ const start = current(this.state).span.start;
455
+ advance(this.state); // consume ^
456
+ advance(this.state); // consume (
457
+ const annotations = this.parseAnnotationArgs();
458
+ expect(this.state, TOKEN_TYPES.RPAREN, 'Expected )', 'RILL-P005');
459
+ const expression = this.parsePrimary();
460
+ return {
461
+ type: 'AnnotatedExpr',
462
+ annotations,
463
+ expression,
464
+ span: makeSpan(start, current(this.state).span.end),
465
+ };
466
+ }
350
467
  // Pass keyword: pass
351
468
  if (check(this.state, TOKEN_TYPES.PASS)) {
352
469
  const token = advance(this.state);
@@ -355,10 +472,6 @@ Parser.prototype.parsePrimary = function () {
355
472
  span: token.span,
356
473
  };
357
474
  }
358
- // Spread operator: *expr
359
- if (check(this.state, TOKEN_TYPES.STAR)) {
360
- return this.parseSpread();
361
- }
362
475
  // Unary minus for negative numbers: -42
363
476
  if (isNegativeNumber(this.state)) {
364
477
  const start = current(this.state).span.start;
@@ -370,10 +483,78 @@ Parser.prototype.parsePrimary = function () {
370
483
  span: makeSpan(start, numToken.span.end),
371
484
  };
372
485
  }
486
+ // Closure sig literal: |param: T, ...|: R
487
+ // Lookahead: PIPE_BAR IDENTIFIER COLON -> sig literal (not a closure body)
488
+ if (isClosureSigLiteralStart(this.state)) {
489
+ return this.parseClosureSigLiteral();
490
+ }
373
491
  // Closure: |params| body or || body
374
492
  if (isClosureStart(this.state)) {
375
493
  return this.parseClosure();
376
494
  }
495
+ // Whitespace adjacency error: collection keyword followed by bracket with whitespace (RILL-P007)
496
+ // e.g. `list [` or `ordered [` — the lexer only emits compound tokens (LIST_LBRACKET etc.)
497
+ // when there is NO whitespace. If whitespace separates them, we get IDENTIFIER + LBRACKET/LT.
498
+ const COMPOUND_KEYWORDS_WITH_BRACKET = ['list', 'dict', 'tuple', 'ordered'];
499
+ const COMPOUND_KEYWORDS_WITH_ANGLE = ['destruct', 'slice'];
500
+ if (check(this.state, TOKEN_TYPES.IDENTIFIER)) {
501
+ const identValue = current(this.state).value;
502
+ const nextTokType = peek(this.state, 1).type;
503
+ if (COMPOUND_KEYWORDS_WITH_BRACKET.includes(identValue) &&
504
+ nextTokType === TOKEN_TYPES.LBRACKET) {
505
+ throw new ParseError('RILL-P007', "keyword and bracket must be adjacent; found whitespace before '['", current(this.state).span.start);
506
+ }
507
+ if (COMPOUND_KEYWORDS_WITH_ANGLE.includes(identValue) &&
508
+ nextTokType === TOKEN_TYPES.LT) {
509
+ throw new ParseError('RILL-P007', "keyword and bracket must be adjacent; found whitespace before '<'", current(this.state).span.start);
510
+ }
511
+ }
512
+ // Removed sigil forms: *[, *<, /<, @$fn (RILL-P009)
513
+ // Note: @[ is handled by AT in parseCommonConstruct as a loop — covered separately below.
514
+ if (check(this.state, TOKEN_TYPES.STAR)) {
515
+ const nextTokType = peek(this.state, 1).type;
516
+ if (nextTokType === TOKEN_TYPES.LBRACKET) {
517
+ throw new ParseError('RILL-P009', 'Sigil syntax *[ was removed; use tuple[...] or ordered[...]', current(this.state).span.start);
518
+ }
519
+ if (nextTokType === TOKEN_TYPES.LT) {
520
+ throw new ParseError('RILL-P009', 'Sigil syntax *< was removed; use destruct<...>', current(this.state).span.start);
521
+ }
522
+ }
523
+ if (check(this.state, TOKEN_TYPES.SLASH)) {
524
+ const nextTokType = peek(this.state, 1).type;
525
+ if (nextTokType === TOKEN_TYPES.LT) {
526
+ throw new ParseError('RILL-P009', 'Sigil syntax /< was removed; use slice<...>', current(this.state).span.start);
527
+ }
528
+ }
529
+ // Keyword-prefixed collection literals: list[...], tuple[...], ordered[...]
530
+ // Note: dict[...] is handled below — it produces a DictNode (same as bare [key:val])
531
+ if (check(this.state, TOKEN_TYPES.LIST_LBRACKET, TOKEN_TYPES.TUPLE_LBRACKET, TOKEN_TYPES.ORDERED_LBRACKET)) {
532
+ const token = advance(this.state);
533
+ const collectionTypeMap = {
534
+ [TOKEN_TYPES.LIST_LBRACKET]: 'list',
535
+ [TOKEN_TYPES.TUPLE_LBRACKET]: 'tuple',
536
+ [TOKEN_TYPES.ORDERED_LBRACKET]: 'ordered',
537
+ };
538
+ const collectionType = collectionTypeMap[token.type];
539
+ return this.parseCollectionLiteral(collectionType);
540
+ }
541
+ // dict[...] in expression context: same semantics as bare [key: val] (DictNode)
542
+ if (check(this.state, TOKEN_TYPES.DICT_LBRACKET)) {
543
+ const start = current(this.state).span.start;
544
+ advance(this.state); // consume dict[
545
+ skipNewlines(this.state);
546
+ // Handle empty dict: dict[]
547
+ if (check(this.state, TOKEN_TYPES.RBRACKET)) {
548
+ const rbracket = advance(this.state); // consume ]
549
+ return {
550
+ type: 'Dict',
551
+ entries: [],
552
+ defaultValue: null,
553
+ span: makeSpan(start, rbracket.span.end),
554
+ };
555
+ }
556
+ return this.parseDict(start);
557
+ }
377
558
  // Literal
378
559
  if (isLiteralStart(this.state)) {
379
560
  return this.parseLiteral();
@@ -390,6 +571,26 @@ Parser.prototype.parsePrimary = function () {
390
571
  if (isMethodCall(this.state)) {
391
572
  return this.parseMethodCall(null);
392
573
  }
574
+ // Type constructor: list(...), dict(...), tuple(...), ordered(...)
575
+ const TYPE_CONSTRUCTORS = ['list', 'dict', 'tuple', 'ordered'];
576
+ if (check(this.state, TOKEN_TYPES.IDENTIFIER) &&
577
+ TYPE_CONSTRUCTORS.includes(current(this.state).value) &&
578
+ this.state.tokens[this.state.pos + 1]?.type === TOKEN_TYPES.LPAREN) {
579
+ const name = current(this.state).value;
580
+ return this.parseTypeConstructor(name);
581
+ }
582
+ // Type name expression: bare type name in expression position (e.g. `number`, `string`)
583
+ // Invalid type names fall through to the host call path (EC-6).
584
+ if (check(this.state, TOKEN_TYPES.IDENTIFIER) &&
585
+ VALID_TYPE_NAMES.includes(current(this.state).value) &&
586
+ this.state.tokens[this.state.pos + 1]?.type !== TOKEN_TYPES.LPAREN) {
587
+ const token = advance(this.state);
588
+ return {
589
+ type: 'TypeNameExpr',
590
+ typeName: token.value,
591
+ span: token.span,
592
+ };
593
+ }
393
594
  // Function call with parens
394
595
  if (isHostCall(this.state)) {
395
596
  return this.parseHostCall();
@@ -398,6 +599,9 @@ Parser.prototype.parsePrimary = function () {
398
599
  if (check(this.state, TOKEN_TYPES.IDENTIFIER)) {
399
600
  return parseBareHostCall(this.state);
400
601
  }
602
+ if (check(this.state, TOKEN_TYPES.LBRACKET)) {
603
+ return this.parseTupleOrDict();
604
+ }
401
605
  // Common constructs
402
606
  const common = this.parseCommonConstruct();
403
607
  if (common)
@@ -422,19 +626,20 @@ Parser.prototype.parsePipeTarget = function () {
422
626
  if (check(this.state, TOKEN_TYPES.ERROR)) {
423
627
  return this.parseError();
424
628
  }
629
+ // Convert operator: -> :>type or -> :>$var or -> :>ordered(...)
630
+ if (check(this.state, TOKEN_TYPES.COLON) &&
631
+ peek(this.state, 1).type === TOKEN_TYPES.GT) {
632
+ return this.parseConvert();
633
+ }
425
634
  // Type operations: -> :type or -> :?type
426
635
  if (check(this.state, TOKEN_TYPES.COLON)) {
427
636
  return this.parseTypeOperation();
428
637
  }
429
- // Spread as pipe target: -> *
430
- if (check(this.state, TOKEN_TYPES.STAR)) {
431
- return this.parseSpreadTarget();
432
- }
433
638
  // Extraction operators
434
- if (check(this.state, TOKEN_TYPES.STAR_LT)) {
435
- return this.parseDestructure();
639
+ if (check(this.state, TOKEN_TYPES.DESTRUCT_LANGLE)) {
640
+ return this.parseDestructTarget();
436
641
  }
437
- if (check(this.state, TOKEN_TYPES.SLASH_LT)) {
642
+ if (check(this.state, TOKEN_TYPES.SLICE_LANGLE)) {
438
643
  return this.parseSlice();
439
644
  }
440
645
  // Collection operators
@@ -454,13 +659,26 @@ Parser.prototype.parsePipeTarget = function () {
454
659
  if (isClosureStart(this.state)) {
455
660
  return this.parseClosure();
456
661
  }
457
- // Method call (possibly chained: .a.b.c)
662
+ // Method call or annotation access (possibly chained: .a.b.c or .^type)
458
663
  if (check(this.state, TOKEN_TYPES.DOT)) {
459
664
  const methods = [];
460
665
  const start = current(this.state).span.start;
461
- // Collect all chained method calls
666
+ // Collect all chained method calls and annotation accesses
462
667
  while (check(this.state, TOKEN_TYPES.DOT)) {
463
- methods.push(this.parseMethodCall(null));
668
+ if (isAnnotationAccess(this.state)) {
669
+ const dotStart = current(this.state).span.start;
670
+ advance(this.state); // consume .
671
+ advance(this.state); // consume ^
672
+ const nameToken = expect(this.state, TOKEN_TYPES.IDENTIFIER, 'Expected annotation key after .^');
673
+ methods.push({
674
+ type: 'AnnotationAccess',
675
+ key: nameToken.value,
676
+ span: makeSpan(dotStart, nameToken.span.end),
677
+ });
678
+ }
679
+ else {
680
+ methods.push(this.parseMethodCall(null));
681
+ }
464
682
  }
465
683
  if (check(this.state, TOKEN_TYPES.QUESTION)) {
466
684
  const postfixExpr = {
@@ -505,10 +723,6 @@ Parser.prototype.parsePipeTarget = function () {
505
723
  if (isClosureCallWithAccess(this.state)) {
506
724
  return this.parseClosureCall();
507
725
  }
508
- // Sequential spread: -> @$var or -> @[closures]
509
- if (isClosureChainTarget(this.state)) {
510
- return this.parseClosureChain();
511
- }
512
726
  // Pipe invoke: -> $() or -> $(args)
513
727
  if (canStartPipeInvoke(this.state)) {
514
728
  return this.parsePipeInvoke();
@@ -522,34 +736,40 @@ Parser.prototype.parsePipeTarget = function () {
522
736
  if (check(this.state, TOKEN_TYPES.STRING)) {
523
737
  return this.parseString();
524
738
  }
525
- // Dict or list literal for dispatch
739
+ // Bare bracket literal as dispatch target: ["a", "b"] or [a: 1]
526
740
  if (check(this.state, TOKEN_TYPES.LBRACKET)) {
527
- const start = current(this.state).span.start;
528
- advance(this.state); // consume [
529
- skipNewlines(this.state);
530
- // Handle empty brackets: [] or [:]
531
- if (check(this.state, TOKEN_TYPES.RBRACKET)) {
532
- advance(this.state); // consume ]
533
- // Check for ?? default value
534
- let defaultValue = null;
535
- if (check(this.state, TOKEN_TYPES.NULLISH_COALESCE)) {
536
- advance(this.state);
537
- defaultValue = this.parseDefaultValue();
538
- }
539
- // Empty brackets [] = empty tuple
741
+ const literal = this.parseTupleOrDict();
742
+ if (check(this.state, TOKEN_TYPES.NULLISH_COALESCE)) {
743
+ advance(this.state);
744
+ const defaultValue = this.parseDefaultValue();
745
+ return { ...literal, defaultValue };
746
+ }
747
+ return literal;
748
+ }
749
+ // Keyword list literal as dispatch target: list["a", "b"]
750
+ if (check(this.state, TOKEN_TYPES.LIST_LBRACKET)) {
751
+ const listStart = current(this.state).span.start;
752
+ advance(this.state); // consume list[
753
+ const listLiteral = this.parseCollectionLiteral('list');
754
+ if (check(this.state, TOKEN_TYPES.NULLISH_COALESCE)) {
755
+ advance(this.state);
756
+ const defaultValue = this.parseDefaultValue();
540
757
  return {
541
- type: 'Tuple',
542
- elements: [],
758
+ ...listLiteral,
543
759
  defaultValue,
544
- span: makeSpan(start, current(this.state).span.end),
760
+ span: makeSpan(listStart, defaultValue.span.end),
545
761
  };
546
762
  }
547
- // Handle empty dict: [:]
548
- if (check(this.state, TOKEN_TYPES.COLON) &&
549
- this.state.tokens[this.state.pos + 1]?.type === TOKEN_TYPES.RBRACKET) {
550
- advance(this.state); // consume :
551
- advance(this.state); // consume ]
552
- // Check for ?? default value
763
+ return listLiteral;
764
+ }
765
+ // Keyword dict literal as dispatch target: dict[key: val, ...]
766
+ if (check(this.state, TOKEN_TYPES.DICT_LBRACKET)) {
767
+ const start = current(this.state).span.start;
768
+ advance(this.state); // consume dict[
769
+ skipNewlines(this.state);
770
+ // Handle empty dict: dict[]
771
+ if (check(this.state, TOKEN_TYPES.RBRACKET)) {
772
+ const rbracket = advance(this.state); // consume ]
553
773
  let defaultValue = null;
554
774
  if (check(this.state, TOKEN_TYPES.NULLISH_COALESCE)) {
555
775
  advance(this.state);
@@ -559,37 +779,16 @@ Parser.prototype.parsePipeTarget = function () {
559
779
  type: 'Dict',
560
780
  entries: [],
561
781
  defaultValue,
562
- span: makeSpan(start, current(this.state).span.end),
782
+ span: makeSpan(start, rbracket.span.end),
563
783
  };
564
784
  }
565
- // Distinguish dict from tuple using isDictStart helper
566
- if (isDictStart(this.state)) {
567
- const dict = this.parseDict(start);
568
- // Check for ?? default value after dict
569
- if (check(this.state, TOKEN_TYPES.NULLISH_COALESCE)) {
570
- advance(this.state);
571
- const defaultValue = this.parseDefaultValue();
572
- return {
573
- ...dict,
574
- defaultValue,
575
- };
576
- }
577
- return dict;
578
- }
579
- else {
580
- // Parse as tuple (list literal)
581
- const tuple = this.parseTuple(start);
582
- // Check for ?? default value after tuple
583
- if (check(this.state, TOKEN_TYPES.NULLISH_COALESCE)) {
584
- advance(this.state);
585
- const defaultValue = this.parseDefaultValue();
586
- return {
587
- ...tuple,
588
- defaultValue,
589
- };
590
- }
591
- return tuple;
785
+ const dict = this.parseDict(start);
786
+ if (check(this.state, TOKEN_TYPES.NULLISH_COALESCE)) {
787
+ advance(this.state);
788
+ const defaultValue = this.parseDefaultValue();
789
+ return { ...dict, defaultValue };
592
790
  }
791
+ return dict;
593
792
  }
594
793
  // Function call with parens
595
794
  if (isHostCall(this.state)) {
@@ -616,15 +815,16 @@ Parser.prototype.parseCapture = function () {
616
815
  const start = current(this.state).span.start;
617
816
  expect(this.state, TOKEN_TYPES.DOLLAR, 'Expected $');
618
817
  const nameToken = expect(this.state, TOKEN_TYPES.IDENTIFIER, 'Expected variable name');
619
- let typeName = null;
818
+ let typeRef = null;
620
819
  if (check(this.state, TOKEN_TYPES.COLON)) {
621
820
  advance(this.state);
622
- typeName = parseTypeName(this.state, VALID_TYPE_NAMES);
821
+ typeRef = parseTypeRef(this.state);
623
822
  }
624
823
  return {
625
824
  type: 'Capture',
626
825
  name: nameToken.value,
627
- typeName,
826
+ typeRef,
827
+ inlineShape: null,
628
828
  span: makeSpan(start, current(this.state).span.end),
629
829
  };
630
830
  };
@@ -687,6 +887,7 @@ Parser.prototype.parseLogicalOr = function () {
687
887
  let left = this.parseLogicalAnd();
688
888
  while (check(this.state, TOKEN_TYPES.OR)) {
689
889
  advance(this.state);
890
+ skipNewlines(this.state);
690
891
  const right = this.parseLogicalAnd();
691
892
  left = {
692
893
  type: 'BinaryExpr',
@@ -703,6 +904,7 @@ Parser.prototype.parseLogicalAnd = function () {
703
904
  let left = this.parseComparison();
704
905
  while (check(this.state, TOKEN_TYPES.AND)) {
705
906
  advance(this.state);
907
+ skipNewlines(this.state);
706
908
  const right = this.parseComparison();
707
909
  left = {
708
910
  type: 'BinaryExpr',
@@ -719,6 +921,7 @@ Parser.prototype.parseComparison = function () {
719
921
  let left = this.parseAdditive();
720
922
  if (this.isComparisonOp()) {
721
923
  const opToken = advance(this.state);
924
+ skipNewlines(this.state);
722
925
  const op = this.tokenToComparisonOp(opToken.type);
723
926
  const right = this.parseAdditive();
724
927
  left = {
@@ -736,6 +939,7 @@ Parser.prototype.parseAdditive = function () {
736
939
  let left = this.parseMultiplicative();
737
940
  while (check(this.state, TOKEN_TYPES.PLUS, TOKEN_TYPES.MINUS)) {
738
941
  const opToken = advance(this.state);
942
+ skipNewlines(this.state);
739
943
  const op = opToken.type === TOKEN_TYPES.PLUS ? '+' : '-';
740
944
  const right = this.parseMultiplicative();
741
945
  left = {
@@ -753,6 +957,7 @@ Parser.prototype.parseMultiplicative = function () {
753
957
  let left = this.parseUnary();
754
958
  while (check(this.state, TOKEN_TYPES.STAR, TOKEN_TYPES.SLASH, TOKEN_TYPES.PERCENT)) {
755
959
  const opToken = advance(this.state);
960
+ skipNewlines(this.state);
756
961
  const op = opToken.type === TOKEN_TYPES.STAR
757
962
  ? '*'
758
963
  : opToken.type === TOKEN_TYPES.SLASH
@@ -794,4 +999,76 @@ Parser.prototype.parseUnary = function () {
794
999
  }
795
1000
  return this.parsePostfixExpr();
796
1001
  };
1002
+ // ============================================================
1003
+ // CLOSURE SIG LITERAL PARSING
1004
+ // ============================================================
1005
+ Parser.prototype.parseClosureSigLiteral = function () {
1006
+ const start = current(this.state).span.start;
1007
+ // Consume opening |
1008
+ expect(this.state, TOKEN_TYPES.PIPE_BAR, 'Expected |');
1009
+ skipNewlines(this.state);
1010
+ const params = [];
1011
+ // Parse param-type-list: name: typeExpr [, name: typeExpr]*
1012
+ while (!check(this.state, TOKEN_TYPES.PIPE_BAR)) {
1013
+ const nameToken = expect(this.state, TOKEN_TYPES.IDENTIFIER, 'Expected parameter name');
1014
+ expect(this.state, TOKEN_TYPES.COLON, 'Expected : after parameter name');
1015
+ skipNewlines(this.state);
1016
+ const typeExpr = this.parseExpression();
1017
+ params.push({ name: nameToken.value, typeExpr });
1018
+ skipNewlines(this.state);
1019
+ if (check(this.state, TOKEN_TYPES.COMMA)) {
1020
+ advance(this.state);
1021
+ skipNewlines(this.state);
1022
+ }
1023
+ }
1024
+ // Consume closing |
1025
+ expect(this.state, TOKEN_TYPES.PIPE_BAR, 'Expected |', 'RILL-P005');
1026
+ // Consume : before return type
1027
+ expect(this.state, TOKEN_TYPES.COLON, 'Expected : before return type in closure sig literal');
1028
+ skipNewlines(this.state);
1029
+ const returnType = this.parseExpression();
1030
+ return {
1031
+ type: 'ClosureSigLiteral',
1032
+ params,
1033
+ returnType,
1034
+ span: makeSpan(start, current(this.state).span.end),
1035
+ };
1036
+ };
1037
+ // ============================================================
1038
+ // CONVERT PARSING
1039
+ // ============================================================
1040
+ /**
1041
+ * Parse the convert operator: :>type, :>$var, or :>ordered(field: type, ...)
1042
+ *
1043
+ * Called when current token is COLON and next is GT (i.e. `:>`).
1044
+ * Consumes COLON and GT, then parses:
1045
+ * - structural: ordered(field: type, ...) → TypeConstructorNode
1046
+ * - dynamic: $varName → TypeRef { kind: 'dynamic' }
1047
+ * - static: list | dict | tuple | ... → TypeRef { kind: 'static' }
1048
+ */
1049
+ Parser.prototype.parseConvert = function () {
1050
+ const start = current(this.state).span.start;
1051
+ expect(this.state, TOKEN_TYPES.COLON, 'Expected :');
1052
+ expect(this.state, TOKEN_TYPES.GT, 'Expected >');
1053
+ // Structural convert: :>ordered(field: type, ...)
1054
+ const TYPE_CONSTRUCTORS = ['list', 'dict', 'tuple', 'ordered'];
1055
+ if (check(this.state, TOKEN_TYPES.IDENTIFIER) &&
1056
+ TYPE_CONSTRUCTORS.includes(current(this.state).value) &&
1057
+ this.state.tokens[this.state.pos + 1]?.type === TOKEN_TYPES.LPAREN) {
1058
+ const constructorName = current(this.state).value;
1059
+ const typeRef = this.parseTypeConstructor(constructorName);
1060
+ return {
1061
+ type: 'Convert',
1062
+ typeRef,
1063
+ span: makeSpan(start, current(this.state).span.end),
1064
+ };
1065
+ }
1066
+ // Dynamic or static type reference
1067
+ const typeRef = parseTypeRef(this.state);
1068
+ return {
1069
+ type: 'Convert',
1070
+ typeRef,
1071
+ span: makeSpan(start, current(this.state).span.end),
1072
+ };
1073
+ };
797
1074
  //# sourceMappingURL=parser-expr.js.map