@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.
- package/dist/ast-nodes.d.ts +189 -49
- package/dist/ast-nodes.d.ts.map +1 -1
- package/dist/ast-unions.d.ts +1 -1
- package/dist/ast-unions.d.ts.map +1 -1
- package/dist/constants.d.ts +14 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +29 -0
- package/dist/constants.js.map +1 -0
- package/dist/error-classes.d.ts +3 -1
- package/dist/error-classes.d.ts.map +1 -1
- package/dist/error-classes.js +11 -5
- package/dist/error-classes.js.map +1 -1
- package/dist/error-registry.d.ts.map +1 -1
- package/dist/error-registry.js +293 -9
- package/dist/error-registry.js.map +1 -1
- package/dist/ext/crypto/index.d.ts +2 -1
- package/dist/ext/crypto/index.d.ts.map +1 -1
- package/dist/ext/crypto/index.js +7 -0
- package/dist/ext/crypto/index.js.map +1 -1
- package/dist/ext/exec/index.d.ts +2 -1
- package/dist/ext/exec/index.d.ts.map +1 -1
- package/dist/ext/exec/index.js +6 -0
- package/dist/ext/exec/index.js.map +1 -1
- package/dist/ext/fetch/index.d.ts +2 -1
- package/dist/ext/fetch/index.d.ts.map +1 -1
- package/dist/ext/fetch/index.js +6 -0
- package/dist/ext/fetch/index.js.map +1 -1
- package/dist/ext/fs/index.d.ts +2 -1
- package/dist/ext/fs/index.d.ts.map +1 -1
- package/dist/ext/fs/index.js +3 -0
- package/dist/ext/fs/index.js.map +1 -1
- package/dist/ext/kv/index.d.ts +48 -12
- package/dist/ext/kv/index.d.ts.map +1 -1
- package/dist/ext/kv/index.js +225 -63
- package/dist/ext/kv/index.js.map +1 -1
- package/dist/ext/kv/store.d.ts +4 -0
- package/dist/ext/kv/store.d.ts.map +1 -1
- package/dist/ext/kv/store.js +14 -0
- package/dist/ext/kv/store.js.map +1 -1
- package/dist/generated/introspection-data.d.ts +1 -1
- package/dist/generated/introspection-data.d.ts.map +1 -1
- package/dist/generated/introspection-data.js +194 -185
- package/dist/generated/introspection-data.js.map +1 -1
- package/dist/generated/version-data.d.ts +1 -1
- package/dist/generated/version-data.js +3 -3
- package/dist/highlight-map.d.ts.map +1 -1
- package/dist/highlight-map.js +8 -2
- package/dist/highlight-map.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/lexer/operators.d.ts.map +1 -1
- package/dist/lexer/operators.js +0 -2
- package/dist/lexer/operators.js.map +1 -1
- package/dist/lexer/readers.d.ts +18 -1
- package/dist/lexer/readers.d.ts.map +1 -1
- package/dist/lexer/readers.js +55 -0
- package/dist/lexer/readers.js.map +1 -1
- package/dist/parser/helpers.d.ts +8 -13
- package/dist/parser/helpers.d.ts.map +1 -1
- package/dist/parser/helpers.js +42 -35
- package/dist/parser/helpers.js.map +1 -1
- package/dist/parser/index.d.ts +1 -0
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +1 -0
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/parser-collect.d.ts.map +1 -1
- package/dist/parser/parser-collect.js +34 -5
- package/dist/parser/parser-collect.js.map +1 -1
- package/dist/parser/parser-control.d.ts.map +1 -1
- package/dist/parser/parser-control.js +9 -0
- package/dist/parser/parser-control.js.map +1 -1
- package/dist/parser/parser-expr.d.ts +3 -1
- package/dist/parser/parser-expr.d.ts.map +1 -1
- package/dist/parser/parser-expr.js +377 -100
- package/dist/parser/parser-expr.js.map +1 -1
- package/dist/parser/parser-extract.d.ts +3 -5
- package/dist/parser/parser-extract.d.ts.map +1 -1
- package/dist/parser/parser-extract.js +37 -69
- package/dist/parser/parser-extract.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 +112 -36
- package/dist/parser/parser-functions.js.map +1 -1
- package/dist/parser/parser-literals.d.ts +4 -3
- package/dist/parser/parser-literals.d.ts.map +1 -1
- package/dist/parser/parser-literals.js +257 -42
- package/dist/parser/parser-literals.js.map +1 -1
- package/dist/parser/parser-script.d.ts.map +1 -1
- package/dist/parser/parser-script.js +25 -12
- package/dist/parser/parser-script.js.map +1 -1
- package/dist/parser/parser-shape.d.ts +13 -0
- package/dist/parser/parser-shape.d.ts.map +1 -0
- package/dist/parser/parser-shape.js +72 -0
- package/dist/parser/parser-shape.js.map +1 -0
- package/dist/parser/parser-types.d.ts +21 -0
- package/dist/parser/parser-types.d.ts.map +1 -0
- package/dist/parser/parser-types.js +30 -0
- package/dist/parser/parser-types.js.map +1 -0
- package/dist/parser/parser-variables.d.ts.map +1 -1
- package/dist/parser/parser-variables.js +10 -1
- package/dist/parser/parser-variables.js.map +1 -1
- package/dist/runtime/core/callable.d.ts +25 -22
- package/dist/runtime/core/callable.d.ts.map +1 -1
- package/dist/runtime/core/callable.js +43 -36
- 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 +8 -8
- package/dist/runtime/core/context.js.map +1 -1
- package/dist/runtime/core/equals.d.ts.map +1 -1
- package/dist/runtime/core/equals.js +142 -30
- package/dist/runtime/core/equals.js.map +1 -1
- package/dist/runtime/core/eval/base.d.ts +2 -2
- package/dist/runtime/core/eval/base.d.ts.map +1 -1
- package/dist/runtime/core/eval/evaluator.d.ts.map +1 -1
- package/dist/runtime/core/eval/evaluator.js +3 -1
- package/dist/runtime/core/eval/evaluator.js.map +1 -1
- package/dist/runtime/core/eval/index.d.ts +16 -1
- package/dist/runtime/core/eval/index.d.ts.map +1 -1
- package/dist/runtime/core/eval/index.js +22 -2
- package/dist/runtime/core/eval/index.js.map +1 -1
- package/dist/runtime/core/eval/mixins/annotations.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/annotations.js +14 -8
- package/dist/runtime/core/eval/mixins/annotations.js.map +1 -1
- package/dist/runtime/core/eval/mixins/closures.d.ts +0 -2
- package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/closures.js +286 -105
- package/dist/runtime/core/eval/mixins/closures.js.map +1 -1
- package/dist/runtime/core/eval/mixins/collections.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/collections.js +65 -25
- package/dist/runtime/core/eval/mixins/collections.js.map +1 -1
- package/dist/runtime/core/eval/mixins/control-flow.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/control-flow.js +21 -17
- package/dist/runtime/core/eval/mixins/control-flow.js.map +1 -1
- package/dist/runtime/core/eval/mixins/conversion.d.ts +30 -0
- package/dist/runtime/core/eval/mixins/conversion.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/conversion.js +203 -0
- package/dist/runtime/core/eval/mixins/conversion.js.map +1 -0
- package/dist/runtime/core/eval/mixins/core.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/core.js +101 -32
- package/dist/runtime/core/eval/mixins/core.js.map +1 -1
- package/dist/runtime/core/eval/mixins/extraction.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/extraction.js +136 -30
- package/dist/runtime/core/eval/mixins/extraction.js.map +1 -1
- package/dist/runtime/core/eval/mixins/list-dispatch.d.ts +17 -0
- package/dist/runtime/core/eval/mixins/list-dispatch.d.ts.map +1 -0
- package/dist/runtime/core/eval/mixins/list-dispatch.js +97 -0
- package/dist/runtime/core/eval/mixins/list-dispatch.js.map +1 -0
- package/dist/runtime/core/eval/mixins/literals.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/literals.js +64 -82
- package/dist/runtime/core/eval/mixins/literals.js.map +1 -1
- package/dist/runtime/core/eval/mixins/types.d.ts +4 -0
- package/dist/runtime/core/eval/mixins/types.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/types.js +202 -3
- package/dist/runtime/core/eval/mixins/types.js.map +1 -1
- package/dist/runtime/core/eval/mixins/variables.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/variables.js +42 -7
- package/dist/runtime/core/eval/mixins/variables.js.map +1 -1
- package/dist/runtime/core/execute.d.ts.map +1 -1
- package/dist/runtime/core/execute.js +2 -15
- package/dist/runtime/core/execute.js.map +1 -1
- package/dist/runtime/core/field-descriptor.d.ts +29 -0
- package/dist/runtime/core/field-descriptor.d.ts.map +1 -0
- package/dist/runtime/core/field-descriptor.js +27 -0
- package/dist/runtime/core/field-descriptor.js.map +1 -0
- package/dist/runtime/core/types.d.ts +15 -6
- package/dist/runtime/core/types.d.ts.map +1 -1
- package/dist/runtime/core/types.js.map +1 -1
- package/dist/runtime/core/values.d.ts +107 -9
- package/dist/runtime/core/values.d.ts.map +1 -1
- package/dist/runtime/core/values.js +453 -43
- package/dist/runtime/core/values.js.map +1 -1
- package/dist/runtime/ext/builtins.d.ts.map +1 -1
- package/dist/runtime/ext/builtins.js +47 -107
- package/dist/runtime/ext/builtins.js.map +1 -1
- package/dist/runtime/ext/extensions.d.ts +134 -1
- package/dist/runtime/ext/extensions.d.ts.map +1 -1
- package/dist/runtime/ext/extensions.js.map +1 -1
- package/dist/runtime/index.d.ts +7 -4
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +7 -2
- package/dist/runtime/index.js.map +1 -1
- package/dist/token-types.d.ts +7 -2
- package/dist/token-types.d.ts.map +1 -1
- package/dist/token-types.js +9 -2
- package/dist/token-types.js.map +1 -1
- package/dist/value-types.d.ts +19 -1
- package/dist/value-types.d.ts.map +1 -1
- 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,
|
|
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 (
|
|
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
|
-
(
|
|
297
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
435
|
-
return this.
|
|
639
|
+
if (check(this.state, TOKEN_TYPES.DESTRUCT_LANGLE)) {
|
|
640
|
+
return this.parseDestructTarget();
|
|
436
641
|
}
|
|
437
|
-
if (check(this.state, TOKEN_TYPES.
|
|
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
|
-
|
|
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
|
-
//
|
|
739
|
+
// Bare bracket literal as dispatch target: ["a", "b"] or [a: 1]
|
|
526
740
|
if (check(this.state, TOKEN_TYPES.LBRACKET)) {
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
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
|
-
|
|
542
|
-
elements: [],
|
|
758
|
+
...listLiteral,
|
|
543
759
|
defaultValue,
|
|
544
|
-
span: makeSpan(
|
|
760
|
+
span: makeSpan(listStart, defaultValue.span.end),
|
|
545
761
|
};
|
|
546
762
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
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,
|
|
782
|
+
span: makeSpan(start, rbracket.span.end),
|
|
563
783
|
};
|
|
564
784
|
}
|
|
565
|
-
|
|
566
|
-
if (
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
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
|
|
818
|
+
let typeRef = null;
|
|
620
819
|
if (check(this.state, TOKEN_TYPES.COLON)) {
|
|
621
820
|
advance(this.state);
|
|
622
|
-
|
|
821
|
+
typeRef = parseTypeRef(this.state);
|
|
623
822
|
}
|
|
624
823
|
return {
|
|
625
824
|
type: 'Capture',
|
|
626
825
|
name: nameToken.value,
|
|
627
|
-
|
|
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
|