@rcrsr/rill 0.13.2 → 0.15.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 (95) hide show
  1. package/dist/ast-nodes.d.ts +2 -0
  2. package/dist/ast-nodes.d.ts.map +1 -1
  3. package/dist/error-classes.d.ts +2 -0
  4. package/dist/error-classes.d.ts.map +1 -1
  5. package/dist/error-classes.js.map +1 -1
  6. package/dist/error-formatter.d.ts +56 -0
  7. package/dist/error-formatter.d.ts.map +1 -0
  8. package/dist/error-formatter.js +277 -0
  9. package/dist/error-formatter.js.map +1 -0
  10. package/dist/error-registry.d.ts.map +1 -1
  11. package/dist/error-registry.js +19 -1
  12. package/dist/error-registry.js.map +1 -1
  13. package/dist/ext/crypto/index.d.ts.map +1 -1
  14. package/dist/ext/crypto/index.js +11 -8
  15. package/dist/ext/crypto/index.js.map +1 -1
  16. package/dist/ext/exec/index.d.ts.map +1 -1
  17. package/dist/ext/exec/index.js +7 -4
  18. package/dist/ext/exec/index.js.map +1 -1
  19. package/dist/ext/fetch/index.d.ts.map +1 -1
  20. package/dist/ext/fetch/index.js +8 -7
  21. package/dist/ext/fetch/index.js.map +1 -1
  22. package/dist/ext/fs/index.d.ts.map +1 -1
  23. package/dist/ext/fs/index.js +25 -24
  24. package/dist/ext/fs/index.js.map +1 -1
  25. package/dist/ext/kv/index.d.ts.map +1 -1
  26. package/dist/ext/kv/index.js +27 -22
  27. package/dist/ext/kv/index.js.map +1 -1
  28. package/dist/generated/version-data.d.ts +1 -1
  29. package/dist/generated/version-data.js +3 -3
  30. package/dist/index.d.ts +2 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +5 -1
  33. package/dist/index.js.map +1 -1
  34. package/dist/parser/parser-shape.js +14 -2
  35. package/dist/parser/parser-shape.js.map +1 -1
  36. package/dist/runtime/core/callable.d.ts +7 -43
  37. package/dist/runtime/core/callable.d.ts.map +1 -1
  38. package/dist/runtime/core/callable.js +7 -21
  39. package/dist/runtime/core/callable.js.map +1 -1
  40. package/dist/runtime/core/context.d.ts +16 -1
  41. package/dist/runtime/core/context.d.ts.map +1 -1
  42. package/dist/runtime/core/context.js +97 -68
  43. package/dist/runtime/core/context.js.map +1 -1
  44. package/dist/runtime/core/eval/index.d.ts.map +1 -1
  45. package/dist/runtime/core/eval/index.js +1 -3
  46. package/dist/runtime/core/eval/index.js.map +1 -1
  47. package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -1
  48. package/dist/runtime/core/eval/mixins/closures.js +185 -114
  49. package/dist/runtime/core/eval/mixins/closures.js.map +1 -1
  50. package/dist/runtime/core/eval/mixins/conversion.d.ts.map +1 -1
  51. package/dist/runtime/core/eval/mixins/conversion.js +190 -10
  52. package/dist/runtime/core/eval/mixins/conversion.js.map +1 -1
  53. package/dist/runtime/core/eval/mixins/extraction.d.ts.map +1 -1
  54. package/dist/runtime/core/eval/mixins/extraction.js +5 -5
  55. package/dist/runtime/core/eval/mixins/extraction.js.map +1 -1
  56. package/dist/runtime/core/eval/mixins/literals.d.ts.map +1 -1
  57. package/dist/runtime/core/eval/mixins/literals.js +18 -28
  58. package/dist/runtime/core/eval/mixins/literals.js.map +1 -1
  59. package/dist/runtime/core/eval/mixins/types.d.ts.map +1 -1
  60. package/dist/runtime/core/eval/mixins/types.js +50 -8
  61. package/dist/runtime/core/eval/mixins/types.js.map +1 -1
  62. package/dist/runtime/core/eval/mixins/use.d.ts.map +1 -1
  63. package/dist/runtime/core/eval/mixins/use.js +24 -4
  64. package/dist/runtime/core/eval/mixins/use.js.map +1 -1
  65. package/dist/runtime/core/eval/mixins/variables.d.ts.map +1 -1
  66. package/dist/runtime/core/eval/mixins/variables.js +2 -2
  67. package/dist/runtime/core/eval/mixins/variables.js.map +1 -1
  68. package/dist/runtime/core/field-descriptor.d.ts +2 -2
  69. package/dist/runtime/core/field-descriptor.d.ts.map +1 -1
  70. package/dist/runtime/core/introspection.d.ts.map +1 -1
  71. package/dist/runtime/core/introspection.js +4 -10
  72. package/dist/runtime/core/introspection.js.map +1 -1
  73. package/dist/runtime/core/resolvers.d.ts +3 -4
  74. package/dist/runtime/core/resolvers.d.ts.map +1 -1
  75. package/dist/runtime/core/resolvers.js +5 -11
  76. package/dist/runtime/core/resolvers.js.map +1 -1
  77. package/dist/runtime/core/types.d.ts +14 -15
  78. package/dist/runtime/core/types.d.ts.map +1 -1
  79. package/dist/runtime/core/types.js.map +1 -1
  80. package/dist/runtime/core/values.d.ts +33 -4
  81. package/dist/runtime/core/values.d.ts.map +1 -1
  82. package/dist/runtime/core/values.js +136 -35
  83. package/dist/runtime/core/values.js.map +1 -1
  84. package/dist/runtime/ext/builtins.d.ts +10 -3
  85. package/dist/runtime/ext/builtins.d.ts.map +1 -1
  86. package/dist/runtime/ext/builtins.js +578 -608
  87. package/dist/runtime/ext/builtins.js.map +1 -1
  88. package/dist/runtime/index.d.ts +4 -4
  89. package/dist/runtime/index.d.ts.map +1 -1
  90. package/dist/runtime/index.js +2 -2
  91. package/dist/runtime/index.js.map +1 -1
  92. package/dist/signature-parser.d.ts.map +1 -1
  93. package/dist/signature-parser.js +3 -1
  94. package/dist/signature-parser.js.map +1 -1
  95. package/package.json +1 -1
@@ -8,7 +8,8 @@
8
8
  */
9
9
  import { callable, isCallable, isDict } from '../core/callable.js';
10
10
  import { RuntimeError } from '../../types.js';
11
- import { deepEquals, formatValue, inferType, isEmpty, isRillIterator, isVector, valueToJSON, } from '../core/values.js';
11
+ import { parseSignatureRegistration } from '../../signature-parser.js';
12
+ import { anyTypeValue, deepEquals, formatValue, inferType, isEmpty, isRillIterator, isVector, rillTypeToTypeValue, valueToJSON, } from '../core/values.js';
12
13
  import { invokeCallable } from '../core/eval/index.js';
13
14
  // ============================================================
14
15
  // ITERATOR HELPERS
@@ -66,12 +67,28 @@ function makeDictIterator(dict, index) {
66
67
  export const BUILTIN_FUNCTIONS = {
67
68
  /** Identity function - returns its argument */
68
69
  identity: {
69
- signature: '|value: any|:any',
70
+ params: [
71
+ {
72
+ name: 'value',
73
+ type: { type: 'any' },
74
+ defaultValue: undefined,
75
+ annotations: {},
76
+ },
77
+ ],
78
+ returnType: anyTypeValue,
70
79
  fn: (args) => args[0] ?? null,
71
80
  },
72
81
  /** Log a value and return it unchanged (passthrough) */
73
82
  log: {
74
- signature: '|message: any|',
83
+ params: [
84
+ {
85
+ name: 'message',
86
+ type: { type: 'any' },
87
+ defaultValue: undefined,
88
+ annotations: {},
89
+ },
90
+ ],
91
+ returnType: anyTypeValue,
75
92
  fn: (args, ctx) => {
76
93
  const value = args[0] ?? null;
77
94
  const message = formatValue(value);
@@ -81,7 +98,15 @@ export const BUILTIN_FUNCTIONS = {
81
98
  },
82
99
  /** Convert any value to JSON string (throws RuntimeError RILL-R004 on closures, tuples, vectors) */
83
100
  json: {
84
- signature: '|value: any|:string',
101
+ params: [
102
+ {
103
+ name: 'value',
104
+ type: { type: 'any' },
105
+ defaultValue: undefined,
106
+ annotations: {},
107
+ },
108
+ ],
109
+ returnType: rillTypeToTypeValue({ type: 'string' }),
85
110
  fn: (args, _ctx, location) => {
86
111
  const value = args[0] ?? null;
87
112
  try {
@@ -104,7 +129,18 @@ export const BUILTIN_FUNCTIONS = {
104
129
  * Dict: enumerate([a: 1]) -> [[index: 0, key: "a", value: 1]]
105
130
  */
106
131
  enumerate: {
107
- signature: '|items: list | dict | string|:list',
132
+ params: [
133
+ {
134
+ name: 'items',
135
+ type: {
136
+ type: 'union',
137
+ members: [{ type: 'list' }, { type: 'dict' }, { type: 'string' }],
138
+ },
139
+ defaultValue: undefined,
140
+ annotations: {},
141
+ },
142
+ ],
143
+ returnType: rillTypeToTypeValue({ type: 'list' }),
108
144
  fn: (args) => {
109
145
  const input = args[0] ?? null;
110
146
  if (Array.isArray(input)) {
@@ -126,7 +162,27 @@ export const BUILTIN_FUNCTIONS = {
126
162
  * range(start, end, step=1) - generates [start, start+step, ...] up to (but not including) end
127
163
  */
128
164
  range: {
129
- signature: '|start: number, stop: number, step: number = 1|:iterator',
165
+ params: [
166
+ {
167
+ name: 'start',
168
+ type: { type: 'number' },
169
+ defaultValue: undefined,
170
+ annotations: {},
171
+ },
172
+ {
173
+ name: 'stop',
174
+ type: { type: 'number' },
175
+ defaultValue: undefined,
176
+ annotations: {},
177
+ },
178
+ {
179
+ name: 'step',
180
+ type: { type: 'number' },
181
+ defaultValue: 1,
182
+ annotations: {},
183
+ },
184
+ ],
185
+ returnType: anyTypeValue,
130
186
  fn: (args, _ctx, location) => {
131
187
  const start = typeof args[0] === 'number' ? args[0] : 0;
132
188
  const end = typeof args[1] === 'number' ? args[1] : 0;
@@ -156,7 +212,21 @@ export const BUILTIN_FUNCTIONS = {
156
212
  * repeat(value, count) - generates value repeated count times
157
213
  */
158
214
  repeat: {
159
- signature: '|value: any, count: number|:iterator',
215
+ params: [
216
+ {
217
+ name: 'value',
218
+ type: { type: 'any' },
219
+ defaultValue: undefined,
220
+ annotations: {},
221
+ },
222
+ {
223
+ name: 'count',
224
+ type: { type: 'number' },
225
+ defaultValue: undefined,
226
+ annotations: {},
227
+ },
228
+ ],
229
+ returnType: anyTypeValue,
160
230
  fn: (args, _ctx, location) => {
161
231
  const value = args[0] ?? '';
162
232
  const count = typeof args[1] === 'number' ? Math.floor(args[1]) : 0;
@@ -187,7 +257,21 @@ export const BUILTIN_FUNCTIONS = {
187
257
  * Non-closure/non-list second arg throws RILL-R040 (EC-14).
188
258
  */
189
259
  chain: {
190
- signature: '|value: any, transform: any|:any',
260
+ params: [
261
+ {
262
+ name: 'value',
263
+ type: { type: 'any' },
264
+ defaultValue: undefined,
265
+ annotations: {},
266
+ },
267
+ {
268
+ name: 'transform',
269
+ type: { type: 'any' },
270
+ defaultValue: undefined,
271
+ annotations: {},
272
+ },
273
+ ],
274
+ returnType: anyTypeValue,
191
275
  fn: async (args, ctx, location) => {
192
276
  // Pipe position: 5 -> chain($closure) sends args=[$closure] with pipeValue=5.
193
277
  // Detect this by checking if there is exactly one arg and a pipe value is set.
@@ -233,608 +317,494 @@ function createComparisonMethod(compare) {
233
317
  return compare(formatValue(receiver), formatValue(arg ?? ''));
234
318
  };
235
319
  }
236
- // receiverTypes convention:
237
- // - Declare explicit types (e.g. ['string', 'list']) when dispatch enforcement is sufficient.
238
- // - Use [] when the method body performs its own type checking (e.g. works across
239
- // multiple types with distinct branches, or produces a custom error message).
320
+ /**
321
+ * Build a RillFunction entry from a method body and its signature string.
322
+ * Wraps `method(receiver, args, ctx, location)` as `fn(args, ctx, location)`
323
+ * where `args[0]` is the receiver. Parses the signature to extract params and
324
+ * returnType so that task 1.4 can use them directly without re-parsing.
325
+ */
326
+ function buildMethodEntry(name, signature, method) {
327
+ const parsed = parseSignatureRegistration(signature, name);
328
+ return {
329
+ params: parsed.params,
330
+ fn: (args, ctx, location) => method(args[0] ?? null, args.slice(1), ctx, location),
331
+ annotations: parsed.description !== undefined
332
+ ? { description: parsed.description }
333
+ : {},
334
+ returnType: parsed.returnType !== undefined
335
+ ? rillTypeToTypeValue(parsed.returnType)
336
+ : anyTypeValue,
337
+ };
338
+ }
240
339
  export const BUILTIN_METHODS = {
241
- /** Get length of string or array */
242
- len: {
243
- signature: '||:number',
244
- receiverTypes: ['string', 'list', 'dict'],
245
- method: (receiver) => {
246
- if (typeof receiver === 'string')
247
- return receiver.length;
248
- if (Array.isArray(receiver))
249
- return receiver.length;
250
- if (receiver && typeof receiver === 'object') {
251
- return Object.keys(receiver).length;
252
- }
253
- return 0;
254
- },
255
- },
256
- /** Trim whitespace from string */
257
- trim: {
258
- signature: '||:string',
259
- receiverTypes: ['string'],
260
- method: (receiver) => formatValue(receiver).trim(),
261
- },
262
- // === Element access methods ===
263
- /** Get first element of array or first char of string */
264
- head: {
265
- signature: '||:any',
266
- receiverTypes: [],
267
- method: (receiver, _args, _ctx, location) => {
268
- if (Array.isArray(receiver)) {
269
- if (receiver.length === 0) {
270
- throw new RuntimeError('RILL-R002', 'Cannot get head of empty list', location);
271
- }
272
- return receiver[0];
273
- }
274
- if (typeof receiver === 'string') {
275
- if (receiver.length === 0) {
276
- throw new RuntimeError('RILL-R002', 'Cannot get head of empty string', location);
277
- }
278
- return receiver[0];
279
- }
280
- throw new RuntimeError('RILL-R003', `head requires list or string, got ${inferType(receiver)}`, location);
281
- },
282
- },
283
- /** Get last element of array or last char of string */
284
- tail: {
285
- signature: '||:any',
286
- receiverTypes: [],
287
- method: (receiver, _args, _ctx, location) => {
288
- if (Array.isArray(receiver)) {
289
- if (receiver.length === 0) {
290
- throw new RuntimeError('RILL-R002', 'Cannot get tail of empty list', location);
291
- }
292
- return receiver[receiver.length - 1];
293
- }
294
- if (typeof receiver === 'string') {
295
- if (receiver.length === 0) {
296
- throw new RuntimeError('RILL-R002', 'Cannot get tail of empty string', location);
297
- }
298
- return receiver[receiver.length - 1];
299
- }
300
- throw new RuntimeError('RILL-R003', `tail requires list or string, got ${inferType(receiver)}`, location);
301
- },
302
- },
303
- /** Get iterator at first position for any collection */
304
- first: {
305
- signature: '||:iterator',
306
- receiverTypes: [],
307
- method: (receiver, _args, _ctx, location) => {
308
- // For iterators, return as-is (identity)
309
- if (isRillIterator(receiver)) {
310
- return receiver;
311
- }
312
- // For lists
313
- if (Array.isArray(receiver)) {
314
- return makeListIterator(receiver, 0);
315
- }
316
- // For strings
317
- if (typeof receiver === 'string') {
318
- return makeStringIterator(receiver, 0);
319
- }
320
- // For dicts
321
- if (isDict(receiver)) {
322
- return makeDictIterator(receiver, 0);
323
- }
324
- throw new RuntimeError('RILL-R003', `first requires list, string, dict, or iterator, got ${inferType(receiver)}`, location);
325
- },
326
- },
327
- /** Get element at index */
328
- at: {
329
- signature: '|index: number|:any',
330
- receiverTypes: [],
331
- method: (receiver, args, _ctx, location) => {
332
- const idx = typeof args[0] === 'number' ? args[0] : 0;
333
- if (Array.isArray(receiver)) {
334
- if (idx < 0 || idx >= receiver.length) {
335
- throw new RuntimeError('RILL-R002', `List index out of bounds: ${idx}`, location);
336
- }
337
- return receiver[idx];
338
- }
339
- if (typeof receiver === 'string') {
340
- if (idx < 0 || idx >= receiver.length) {
341
- throw new RuntimeError('RILL-R002', `String index out of bounds: ${idx}`, location);
342
- }
343
- return receiver[idx];
344
- }
345
- throw new RuntimeError('RILL-R003', `Cannot call .at() on ${typeof receiver}`, location);
346
- },
347
- },
348
- // === String operations ===
349
- /** Split string by separator (default: newline) */
350
- split: {
351
- signature: '|separator: string = "\\n"|:list',
352
- receiverTypes: ['string'],
353
- method: (receiver, args) => {
354
- const str = formatValue(receiver);
355
- const sep = typeof args[0] === 'string' ? args[0] : '\n';
356
- return str.split(sep);
357
- },
358
- },
359
- /** Join array elements with separator (default: comma) */
360
- join: {
361
- signature: '|separator: string = ","|:string',
362
- receiverTypes: ['list'],
363
- method: (receiver, args) => {
364
- const sep = typeof args[0] === 'string' ? args[0] : ',';
365
- if (!Array.isArray(receiver))
366
- return formatValue(receiver);
367
- return receiver.map(formatValue).join(sep);
368
- },
369
- },
370
- /** Split string into lines (same as .split but newline only) */
371
- lines: {
372
- signature: '||:list',
373
- receiverTypes: ['string'],
374
- method: (receiver) => {
375
- const str = formatValue(receiver);
376
- return str.split('\n');
377
- },
378
- },
379
- // === Utility methods ===
380
- /** Check if value is empty */
381
- empty: {
382
- signature: '||:bool',
383
- receiverTypes: ['string', 'list', 'dict', 'bool', 'number'],
384
- method: (receiver) => isEmpty(receiver),
385
- },
386
- // === String methods ===
387
- /** Check if string starts with prefix */
388
- starts_with: {
389
- signature: '|prefix: string|:bool',
390
- receiverTypes: ['string'],
391
- method: (receiver, args) => {
392
- const str = formatValue(receiver);
393
- const prefix = formatValue(args[0] ?? '');
394
- return str.startsWith(prefix);
395
- },
396
- },
397
- /** Check if string ends with suffix */
398
- ends_with: {
399
- signature: '|suffix: string|:bool',
400
- receiverTypes: ['string'],
401
- method: (receiver, args) => {
402
- const str = formatValue(receiver);
403
- const suffix = formatValue(args[0] ?? '');
404
- return str.endsWith(suffix);
405
- },
406
- },
407
- /** Convert string to lowercase */
408
- lower: {
409
- signature: '||:string',
410
- receiverTypes: ['string'],
411
- method: (receiver) => formatValue(receiver).toLowerCase(),
412
- },
413
- /** Convert string to uppercase */
414
- upper: {
415
- signature: '||:string',
416
- receiverTypes: ['string'],
417
- method: (receiver) => formatValue(receiver).toUpperCase(),
418
- },
419
- /** Replace first regex match */
420
- replace: {
421
- signature: '|pattern: string, replacement: string|:string',
422
- receiverTypes: ['string'],
423
- method: (receiver, args) => {
424
- const str = formatValue(receiver);
425
- const pattern = formatValue(args[0] ?? '');
426
- const replacement = formatValue(args[1] ?? '');
427
- try {
428
- return str.replace(new RegExp(pattern), replacement);
429
- }
430
- catch {
431
- return str;
432
- }
433
- },
434
- },
435
- /** Replace all regex matches */
436
- replace_all: {
437
- signature: '|pattern: string, replacement: string|:string',
438
- receiverTypes: ['string'],
439
- method: (receiver, args) => {
440
- const str = formatValue(receiver);
441
- const pattern = formatValue(args[0] ?? '');
442
- const replacement = formatValue(args[1] ?? '');
443
- try {
444
- return str.replace(new RegExp(pattern, 'g'), replacement);
445
- }
446
- catch {
447
- return str;
448
- }
449
- },
450
- },
451
- /** Check if string contains substring */
452
- contains: {
453
- signature: '|search: string|:bool',
454
- receiverTypes: ['string'],
455
- method: (receiver, args) => {
456
- const str = formatValue(receiver);
457
- const search = formatValue(args[0] ?? '');
458
- return str.includes(search);
459
- },
460
- },
461
- /**
462
- * First regex match info, or empty dict if no match.
463
- * Returns: [matched: string, index: number, groups: []]
464
- */
465
- match: {
466
- signature: '|pattern: string|:dict',
467
- receiverTypes: ['string'],
468
- method: (receiver, args) => {
469
- const str = formatValue(receiver);
470
- const pattern = formatValue(args[0] ?? '');
471
- try {
472
- const m = new RegExp(pattern).exec(str);
473
- if (!m)
474
- return {};
475
- return {
476
- matched: m[0],
477
- index: m.index,
478
- groups: m.slice(1),
479
- };
480
- }
481
- catch {
482
- return {};
483
- }
484
- },
485
- },
486
- /** True if regex matches anywhere in string */
487
- is_match: {
488
- signature: '|pattern: string|:bool',
489
- receiverTypes: ['string'],
490
- method: (receiver, args) => {
491
- const str = formatValue(receiver);
492
- const pattern = formatValue(args[0] ?? '');
493
- try {
494
- return new RegExp(pattern).test(str);
495
- }
496
- catch {
497
- return false;
498
- }
499
- },
500
- },
501
- /** Position of first substring occurrence (-1 if not found) */
502
- index_of: {
503
- signature: '|search: string|:number',
504
- receiverTypes: ['string'],
505
- method: (receiver, args) => {
506
- const str = formatValue(receiver);
507
- const search = formatValue(args[0] ?? '');
508
- return str.indexOf(search);
509
- },
510
- },
511
- /** Repeat string n times */
512
- repeat: {
513
- signature: '|count: number|:string',
514
- receiverTypes: ['string'],
515
- method: (receiver, args) => {
516
- const str = formatValue(receiver);
517
- const n = typeof args[0] === 'number' ? Math.max(0, Math.floor(args[0])) : 0;
518
- return str.repeat(n);
519
- },
520
- },
521
- /** Pad start to length with fill string */
522
- pad_start: {
523
- signature: '|length: number, fill: string = " "|:string',
524
- receiverTypes: ['string'],
525
- method: (receiver, args) => {
526
- const str = formatValue(receiver);
527
- const length = typeof args[0] === 'number' ? args[0] : str.length;
528
- const fill = typeof args[1] === 'string' ? args[1] : ' ';
529
- return str.padStart(length, fill);
530
- },
531
- },
532
- /** Pad end to length with fill string */
533
- pad_end: {
534
- signature: '|length: number, fill: string = " "|:string',
535
- receiverTypes: ['string'],
536
- method: (receiver, args) => {
537
- const str = formatValue(receiver);
538
- const length = typeof args[0] === 'number' ? args[0] : str.length;
539
- const fill = typeof args[1] === 'string' ? args[1] : ' ';
540
- return str.padEnd(length, fill);
541
- },
542
- },
543
- // === Comparison methods ===
544
- /** Equality check (deep structural comparison) */
545
- eq: {
546
- signature: '|other: any|:bool',
547
- receiverTypes: [],
548
- method: (receiver, args) => deepEquals(receiver, args[0] ?? null),
549
- },
550
- /** Inequality check (deep structural comparison) */
551
- ne: {
552
- signature: '|other: any|:bool',
553
- receiverTypes: [],
554
- method: (receiver, args) => !deepEquals(receiver, args[0] ?? null),
555
- },
556
- /** Less than */
557
- lt: {
558
- signature: '|other: any|:bool',
559
- receiverTypes: ['number', 'string'],
560
- method: createComparisonMethod((a, b) => a < b),
561
- },
562
- /** Greater than */
563
- gt: {
564
- signature: '|other: any|:bool',
565
- receiverTypes: ['number', 'string'],
566
- method: createComparisonMethod((a, b) => a > b),
567
- },
568
- /** Less than or equal */
569
- le: {
570
- signature: '|other: any|:bool',
571
- receiverTypes: ['number', 'string'],
572
- method: createComparisonMethod((a, b) => a <= b),
573
- },
574
- /** Greater than or equal */
575
- ge: {
576
- signature: '|other: any|:bool',
577
- receiverTypes: ['number', 'string'],
578
- method: createComparisonMethod((a, b) => a >= b),
579
- },
580
- // === Dict methods (reserved) ===
581
- /** Get all keys of a dict as a tuple of strings */
582
- keys: {
583
- signature: '||:list',
584
- receiverTypes: [],
585
- method: (receiver) => {
586
- if (isDict(receiver)) {
587
- return Object.keys(receiver);
588
- }
589
- return [];
590
- },
591
- },
592
- /** Get all values of a dict as a tuple */
593
- values: {
594
- signature: '||:list',
595
- receiverTypes: [],
596
- method: (receiver) => {
597
- if (isDict(receiver)) {
598
- return Object.values(receiver);
599
- }
600
- return [];
601
- },
602
- },
603
- /** Get all entries of a dict as a tuple of [key, value] pairs */
604
- entries: {
605
- signature: '||:list',
606
- receiverTypes: [],
607
- method: (receiver) => {
608
- if (isDict(receiver)) {
609
- return Object.entries(receiver).map(([k, v]) => [k, v]);
610
- }
611
- return [];
612
- },
613
- },
614
- // === List membership methods ===
615
- /** Check if list contains value (deep equality) */
616
- has: {
617
- signature: '|value: any|:bool',
618
- receiverTypes: [],
619
- method: (receiver, args, _ctx, location) => {
620
- if (!Array.isArray(receiver)) {
621
- throw new RuntimeError('RILL-R003', `has() requires list receiver, got ${inferType(receiver)}`, location);
622
- }
623
- if (args.length !== 1) {
624
- throw new RuntimeError('RILL-R001', `has() expects 1 argument, got ${args.length}`, location);
625
- }
626
- const searchValue = args[0] ?? null;
627
- for (const item of receiver) {
628
- if (deepEquals(item, searchValue)) {
629
- return true;
630
- }
631
- }
632
- return false;
633
- },
634
- },
635
- /** Check if list contains any value from candidates (deep equality) */
636
- has_any: {
637
- signature: '|candidates: list|:bool',
638
- receiverTypes: [],
639
- method: (receiver, args, _ctx, location) => {
640
- if (!Array.isArray(receiver)) {
641
- throw new RuntimeError('RILL-R003', `has_any() requires list receiver, got ${inferType(receiver)}`, location);
642
- }
643
- if (args.length !== 1) {
644
- throw new RuntimeError('RILL-R001', `has_any() expects 1 argument, got ${args.length}`, location);
645
- }
646
- const candidates = args[0] ?? null;
647
- if (!Array.isArray(candidates)) {
648
- throw new RuntimeError('RILL-R001', `has_any() expects list argument, got ${inferType(candidates)}`, location);
649
- }
650
- // Short-circuit on first match
651
- for (const candidate of candidates) {
652
- for (const item of receiver) {
653
- if (deepEquals(item, candidate)) {
654
- return true;
655
- }
656
- }
657
- }
658
- return false;
659
- },
660
- },
661
- /** Check if list contains all values from candidates (deep equality) */
662
- has_all: {
663
- signature: '|candidates: list|:bool',
664
- receiverTypes: [],
665
- method: (receiver, args, _ctx, location) => {
666
- if (!Array.isArray(receiver)) {
667
- throw new RuntimeError('RILL-R003', `has_all() requires list receiver, got ${inferType(receiver)}`, location);
668
- }
669
- if (args.length !== 1) {
670
- throw new RuntimeError('RILL-R001', `has_all() expects 1 argument, got ${args.length}`, location);
671
- }
672
- const candidates = args[0] ?? null;
673
- if (!Array.isArray(candidates)) {
674
- throw new RuntimeError('RILL-R001', `has_all() expects list argument, got ${inferType(candidates)}`, location);
675
- }
676
- // Short-circuit on first mismatch
677
- for (const candidate of candidates) {
678
- let found = false;
679
- for (const item of receiver) {
680
- if (deepEquals(item, candidate)) {
681
- found = true;
682
- break;
683
- }
684
- }
685
- if (!found) {
686
- return false;
687
- }
688
- }
340
+ string: null,
341
+ list: null,
342
+ dict: null,
343
+ number: null,
344
+ bool: null,
345
+ vector: null,
346
+ };
347
+ // ============================================================
348
+ // METHOD BODIES
349
+ // Defined as named RillMethod constants so they can be shared
350
+ // across type groups (e.g. len appears in string, list, dict).
351
+ // ============================================================
352
+ /** Get length of string, list, or dict */
353
+ const mLen = (receiver) => {
354
+ if (typeof receiver === 'string')
355
+ return receiver.length;
356
+ if (Array.isArray(receiver))
357
+ return receiver.length;
358
+ if (receiver && typeof receiver === 'object') {
359
+ return Object.keys(receiver).length;
360
+ }
361
+ return 0;
362
+ };
363
+ /** Trim whitespace from string */
364
+ const mTrim = (receiver) => formatValue(receiver).trim();
365
+ /** Get first element of list or first char of string */
366
+ const mHead = (receiver, _args, _ctx, location) => {
367
+ if (Array.isArray(receiver)) {
368
+ if (receiver.length === 0) {
369
+ throw new RuntimeError('RILL-R002', 'Cannot get head of empty list', location);
370
+ }
371
+ return receiver[0];
372
+ }
373
+ if (typeof receiver === 'string') {
374
+ if (receiver.length === 0) {
375
+ throw new RuntimeError('RILL-R002', 'Cannot get head of empty string', location);
376
+ }
377
+ return receiver[0];
378
+ }
379
+ throw new RuntimeError('RILL-R003', `head requires list or string, got ${inferType(receiver)}`, location);
380
+ };
381
+ /** Get last element of list or last char of string */
382
+ const mTail = (receiver, _args, _ctx, location) => {
383
+ if (Array.isArray(receiver)) {
384
+ if (receiver.length === 0) {
385
+ throw new RuntimeError('RILL-R002', 'Cannot get tail of empty list', location);
386
+ }
387
+ return receiver[receiver.length - 1];
388
+ }
389
+ if (typeof receiver === 'string') {
390
+ if (receiver.length === 0) {
391
+ throw new RuntimeError('RILL-R002', 'Cannot get tail of empty string', location);
392
+ }
393
+ return receiver[receiver.length - 1];
394
+ }
395
+ throw new RuntimeError('RILL-R003', `tail requires list or string, got ${inferType(receiver)}`, location);
396
+ };
397
+ /** Get iterator at first position for any collection */
398
+ const mFirst = (receiver, _args, _ctx, location) => {
399
+ if (isRillIterator(receiver))
400
+ return receiver;
401
+ if (Array.isArray(receiver))
402
+ return makeListIterator(receiver, 0);
403
+ if (typeof receiver === 'string')
404
+ return makeStringIterator(receiver, 0);
405
+ if (isDict(receiver))
406
+ return makeDictIterator(receiver, 0);
407
+ throw new RuntimeError('RILL-R003', `first requires list, string, dict, or iterator, got ${inferType(receiver)}`, location);
408
+ };
409
+ /** Get element at index */
410
+ const mAt = (receiver, args, _ctx, location) => {
411
+ const idx = typeof args[0] === 'number' ? args[0] : 0;
412
+ if (Array.isArray(receiver)) {
413
+ if (idx < 0 || idx >= receiver.length) {
414
+ throw new RuntimeError('RILL-R002', `List index out of bounds: ${idx}`, location);
415
+ }
416
+ return receiver[idx];
417
+ }
418
+ if (typeof receiver === 'string') {
419
+ if (idx < 0 || idx >= receiver.length) {
420
+ throw new RuntimeError('RILL-R002', `String index out of bounds: ${idx}`, location);
421
+ }
422
+ return receiver[idx];
423
+ }
424
+ throw new RuntimeError('RILL-R003', `Cannot call .at() on ${typeof receiver}`, location);
425
+ };
426
+ /** Split string by separator */
427
+ const mSplit = (receiver, args) => {
428
+ const str = formatValue(receiver);
429
+ const sep = typeof args[0] === 'string' ? args[0] : '\n';
430
+ return str.split(sep);
431
+ };
432
+ /** Join list elements with separator */
433
+ const mJoin = (receiver, args) => {
434
+ const sep = typeof args[0] === 'string' ? args[0] : ',';
435
+ if (!Array.isArray(receiver))
436
+ return formatValue(receiver);
437
+ return receiver.map(formatValue).join(sep);
438
+ };
439
+ /** Split string into lines */
440
+ const mLines = (receiver) => formatValue(receiver).split('\n');
441
+ /** Check if value is empty */
442
+ const mEmpty = (receiver) => isEmpty(receiver);
443
+ /** Check if string starts with prefix */
444
+ const mStartsWith = (receiver, args) => formatValue(receiver).startsWith(formatValue(args[0] ?? ''));
445
+ /** Check if string ends with suffix */
446
+ const mEndsWith = (receiver, args) => formatValue(receiver).endsWith(formatValue(args[0] ?? ''));
447
+ /** Convert string to lowercase */
448
+ const mLower = (receiver) => formatValue(receiver).toLowerCase();
449
+ /** Convert string to uppercase */
450
+ const mUpper = (receiver) => formatValue(receiver).toUpperCase();
451
+ /** Replace first regex match */
452
+ const mReplace = (receiver, args) => {
453
+ const str = formatValue(receiver);
454
+ const pattern = formatValue(args[0] ?? '');
455
+ const replacement = formatValue(args[1] ?? '');
456
+ try {
457
+ return str.replace(new RegExp(pattern), replacement);
458
+ }
459
+ catch {
460
+ return str;
461
+ }
462
+ };
463
+ /** Replace all regex matches */
464
+ const mReplaceAll = (receiver, args) => {
465
+ const str = formatValue(receiver);
466
+ const pattern = formatValue(args[0] ?? '');
467
+ const replacement = formatValue(args[1] ?? '');
468
+ try {
469
+ return str.replace(new RegExp(pattern, 'g'), replacement);
470
+ }
471
+ catch {
472
+ return str;
473
+ }
474
+ };
475
+ /** Check if string contains substring */
476
+ const mContains = (receiver, args) => formatValue(receiver).includes(formatValue(args[0] ?? ''));
477
+ /** First regex match info, or empty dict if no match */
478
+ const mMatch = (receiver, args) => {
479
+ const str = formatValue(receiver);
480
+ const pattern = formatValue(args[0] ?? '');
481
+ try {
482
+ const m = new RegExp(pattern).exec(str);
483
+ if (!m)
484
+ return {};
485
+ return { matched: m[0], index: m.index, groups: m.slice(1) };
486
+ }
487
+ catch {
488
+ return {};
489
+ }
490
+ };
491
+ /** True if regex matches anywhere in string */
492
+ const mIsMatch = (receiver, args) => {
493
+ const str = formatValue(receiver);
494
+ const pattern = formatValue(args[0] ?? '');
495
+ try {
496
+ return new RegExp(pattern).test(str);
497
+ }
498
+ catch {
499
+ return false;
500
+ }
501
+ };
502
+ /** Position of first substring occurrence (-1 if not found) */
503
+ const mIndexOf = (receiver, args) => formatValue(receiver).indexOf(formatValue(args[0] ?? ''));
504
+ /** Repeat string n times */
505
+ const mRepeat = (receiver, args) => {
506
+ const str = formatValue(receiver);
507
+ const n = typeof args[0] === 'number' ? Math.max(0, Math.floor(args[0])) : 0;
508
+ return str.repeat(n);
509
+ };
510
+ /** Pad start to length with fill string */
511
+ const mPadStart = (receiver, args) => {
512
+ const str = formatValue(receiver);
513
+ const length = typeof args[0] === 'number' ? args[0] : str.length;
514
+ const fill = typeof args[1] === 'string' ? args[1] : ' ';
515
+ return str.padStart(length, fill);
516
+ };
517
+ /** Pad end to length with fill string */
518
+ const mPadEnd = (receiver, args) => {
519
+ const str = formatValue(receiver);
520
+ const length = typeof args[0] === 'number' ? args[0] : str.length;
521
+ const fill = typeof args[1] === 'string' ? args[1] : ' ';
522
+ return str.padEnd(length, fill);
523
+ };
524
+ /** Equality check (deep structural comparison) */
525
+ const mEq = (receiver, args) => deepEquals(receiver, args[0] ?? null);
526
+ /** Inequality check (deep structural comparison) */
527
+ const mNe = (receiver, args) => !deepEquals(receiver, args[0] ?? null);
528
+ const mLt = createComparisonMethod((a, b) => a < b);
529
+ const mGt = createComparisonMethod((a, b) => a > b);
530
+ const mLe = createComparisonMethod((a, b) => a <= b);
531
+ const mGe = createComparisonMethod((a, b) => a >= b);
532
+ /** Get all keys of a dict as a list */
533
+ const mKeys = (receiver) => isDict(receiver) ? Object.keys(receiver) : [];
534
+ /** Get all values of a dict as a list */
535
+ const mValues = (receiver) => isDict(receiver) ? Object.values(receiver) : [];
536
+ /** Get all entries of a dict as a list of [key, value] pairs */
537
+ const mEntries = (receiver) => isDict(receiver) ? Object.entries(receiver).map(([k, v]) => [k, v]) : [];
538
+ /** Check if list contains value (deep equality) */
539
+ const mHas = (receiver, args, _ctx, location) => {
540
+ if (!Array.isArray(receiver)) {
541
+ throw new RuntimeError('RILL-R003', `has() requires list receiver, got ${inferType(receiver)}`, location);
542
+ }
543
+ if (args.length !== 1) {
544
+ throw new RuntimeError('RILL-R001', `has() expects 1 argument, got ${args.length}`, location);
545
+ }
546
+ const searchValue = args[0] ?? null;
547
+ for (const item of receiver) {
548
+ if (deepEquals(item, searchValue))
689
549
  return true;
690
- },
691
- },
692
- // === Vector methods ===
693
- /** Get number of dimensions in vector */
694
- dimensions: {
695
- signature: '||:number',
696
- receiverTypes: [],
697
- method: (receiver, _args, _ctx, location) => {
698
- if (!isVector(receiver)) {
699
- throw new RuntimeError('RILL-R003', `dimensions requires vector receiver, got ${inferType(receiver)}`, location);
700
- }
701
- return receiver.data.length;
702
- },
703
- },
704
- /** Get model name of vector */
705
- model: {
706
- signature: '||:string',
707
- receiverTypes: [],
708
- method: (receiver, _args, _ctx, location) => {
709
- if (!isVector(receiver)) {
710
- throw new RuntimeError('RILL-R003', `model requires vector receiver, got ${inferType(receiver)}`, location);
711
- }
712
- return receiver.model;
713
- },
714
- },
715
- /** Calculate cosine similarity between two vectors (range [-1, 1]) */
716
- similarity: {
717
- signature: '|other: any|:number',
718
- receiverTypes: [],
719
- method: (receiver, args, _ctx, location) => {
720
- if (!isVector(receiver)) {
721
- throw new RuntimeError('RILL-R003', `similarity requires vector receiver, got ${inferType(receiver)}`, location);
722
- }
723
- const other = args[0] ?? null;
724
- if (!isVector(other)) {
725
- throw new RuntimeError('RILL-R003', `expected vector, got ${inferType(other)}`, location);
726
- }
727
- if (receiver.data.length !== other.data.length) {
728
- throw new RuntimeError('RILL-R003', `vector dimension mismatch: ${receiver.data.length} vs ${other.data.length}`, location);
729
- }
730
- // Cosine similarity: dot(a, b) / (norm(a) * norm(b))
731
- let dotProduct = 0;
732
- let normA = 0;
733
- let normB = 0;
734
- for (let i = 0; i < receiver.data.length; i++) {
735
- const a = receiver.data[i];
736
- const b = other.data[i];
737
- dotProduct += a * b;
738
- normA += a * a;
739
- normB += b * b;
740
- }
741
- const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
742
- if (magnitude === 0)
743
- return 0;
744
- return dotProduct / magnitude;
745
- },
746
- },
747
- /** Calculate dot product between two vectors */
748
- dot: {
749
- signature: '|other: any|:number',
750
- receiverTypes: [],
751
- method: (receiver, args, _ctx, location) => {
752
- if (!isVector(receiver)) {
753
- throw new RuntimeError('RILL-R003', `dot requires vector receiver, got ${inferType(receiver)}`, location);
754
- }
755
- const other = args[0] ?? null;
756
- if (!isVector(other)) {
757
- throw new RuntimeError('RILL-R003', `expected vector, got ${inferType(other)}`, location);
758
- }
759
- if (receiver.data.length !== other.data.length) {
760
- throw new RuntimeError('RILL-R003', `vector dimension mismatch: ${receiver.data.length} vs ${other.data.length}`, location);
761
- }
762
- let result = 0;
763
- for (let i = 0; i < receiver.data.length; i++) {
764
- result += receiver.data[i] * other.data[i];
765
- }
766
- return result;
767
- },
768
- },
769
- /** Calculate Euclidean distance between two vectors (>= 0) */
770
- distance: {
771
- signature: '|other: any|:number',
772
- receiverTypes: [],
773
- method: (receiver, args, _ctx, location) => {
774
- if (!isVector(receiver)) {
775
- throw new RuntimeError('RILL-R003', `distance requires vector receiver, got ${inferType(receiver)}`, location);
776
- }
777
- const other = args[0] ?? null;
778
- if (!isVector(other)) {
779
- throw new RuntimeError('RILL-R003', `expected vector, got ${inferType(other)}`, location);
780
- }
781
- if (receiver.data.length !== other.data.length) {
782
- throw new RuntimeError('RILL-R003', `vector dimension mismatch: ${receiver.data.length} vs ${other.data.length}`, location);
783
- }
784
- let sumSquares = 0;
785
- for (let i = 0; i < receiver.data.length; i++) {
786
- const diff = receiver.data[i] - other.data[i];
787
- sumSquares += diff * diff;
788
- }
789
- return Math.sqrt(sumSquares);
790
- },
791
- },
792
- /** Calculate L2 norm (magnitude) of vector */
793
- norm: {
794
- signature: '||:number',
795
- receiverTypes: [],
796
- method: (receiver, _args, _ctx, location) => {
797
- if (!isVector(receiver)) {
798
- throw new RuntimeError('RILL-R003', `norm requires vector receiver, got ${inferType(receiver)}`, location);
799
- }
800
- let sumSquares = 0;
801
- for (let i = 0; i < receiver.data.length; i++) {
802
- const val = receiver.data[i];
803
- sumSquares += val * val;
804
- }
805
- return Math.sqrt(sumSquares);
806
- },
807
- },
808
- /** Create unit vector (preserves model) */
809
- normalize: {
810
- signature: '||:any',
811
- receiverTypes: [],
812
- method: (receiver, _args, _ctx, location) => {
813
- if (!isVector(receiver)) {
814
- throw new RuntimeError('RILL-R003', `normalize requires vector receiver, got ${inferType(receiver)}`, location);
815
- }
816
- // Calculate norm
817
- let sumSquares = 0;
818
- for (let i = 0; i < receiver.data.length; i++) {
819
- const val = receiver.data[i];
820
- sumSquares += val * val;
821
- }
822
- const magnitude = Math.sqrt(sumSquares);
823
- // If zero vector, return as-is
824
- if (magnitude === 0) {
825
- return receiver;
826
- }
827
- // Create normalized vector
828
- const normalized = new Float32Array(receiver.data.length);
829
- for (let i = 0; i < receiver.data.length; i++) {
830
- normalized[i] = receiver.data[i] / magnitude;
550
+ }
551
+ return false;
552
+ };
553
+ /** Check if list contains any value from candidates (deep equality) */
554
+ const mHasAny = (receiver, args, _ctx, location) => {
555
+ if (!Array.isArray(receiver)) {
556
+ throw new RuntimeError('RILL-R003', `has_any() requires list receiver, got ${inferType(receiver)}`, location);
557
+ }
558
+ if (args.length !== 1) {
559
+ throw new RuntimeError('RILL-R001', `has_any() expects 1 argument, got ${args.length}`, location);
560
+ }
561
+ const candidates = args[0] ?? null;
562
+ if (!Array.isArray(candidates)) {
563
+ throw new RuntimeError('RILL-R001', `has_any() expects list argument, got ${inferType(candidates)}`, location);
564
+ }
565
+ for (const candidate of candidates) {
566
+ for (const item of receiver) {
567
+ if (deepEquals(item, candidate))
568
+ return true;
569
+ }
570
+ }
571
+ return false;
572
+ };
573
+ /** Check if list contains all values from candidates (deep equality) */
574
+ const mHasAll = (receiver, args, _ctx, location) => {
575
+ if (!Array.isArray(receiver)) {
576
+ throw new RuntimeError('RILL-R003', `has_all() requires list receiver, got ${inferType(receiver)}`, location);
577
+ }
578
+ if (args.length !== 1) {
579
+ throw new RuntimeError('RILL-R001', `has_all() expects 1 argument, got ${args.length}`, location);
580
+ }
581
+ const candidates = args[0] ?? null;
582
+ if (!Array.isArray(candidates)) {
583
+ throw new RuntimeError('RILL-R001', `has_all() expects list argument, got ${inferType(candidates)}`, location);
584
+ }
585
+ for (const candidate of candidates) {
586
+ let found = false;
587
+ for (const item of receiver) {
588
+ if (deepEquals(item, candidate)) {
589
+ found = true;
590
+ break;
831
591
  }
832
- return {
833
- __rill_vector: true,
834
- data: normalized,
835
- model: receiver.model,
836
- };
837
- },
838
- },
592
+ }
593
+ if (!found)
594
+ return false;
595
+ }
596
+ return true;
839
597
  };
598
+ /** Get number of dimensions in vector */
599
+ const mDimensions = (receiver, _args, _ctx, location) => {
600
+ if (!isVector(receiver)) {
601
+ throw new RuntimeError('RILL-R003', `dimensions requires vector receiver, got ${inferType(receiver)}`, location);
602
+ }
603
+ return receiver.data.length;
604
+ };
605
+ /** Get model name of vector */
606
+ const mModel = (receiver, _args, _ctx, location) => {
607
+ if (!isVector(receiver)) {
608
+ throw new RuntimeError('RILL-R003', `model requires vector receiver, got ${inferType(receiver)}`, location);
609
+ }
610
+ return receiver.model;
611
+ };
612
+ /** Calculate cosine similarity between two vectors (range [-1, 1]) */
613
+ const mSimilarity = (receiver, args, _ctx, location) => {
614
+ if (!isVector(receiver)) {
615
+ throw new RuntimeError('RILL-R003', `similarity requires vector receiver, got ${inferType(receiver)}`, location);
616
+ }
617
+ const other = args[0] ?? null;
618
+ if (!isVector(other)) {
619
+ throw new RuntimeError('RILL-R003', `expected vector, got ${inferType(other)}`, location);
620
+ }
621
+ if (receiver.data.length !== other.data.length) {
622
+ throw new RuntimeError('RILL-R003', `vector dimension mismatch: ${receiver.data.length} vs ${other.data.length}`, location);
623
+ }
624
+ let dotProduct = 0;
625
+ let normA = 0;
626
+ let normB = 0;
627
+ for (let i = 0; i < receiver.data.length; i++) {
628
+ const a = receiver.data[i];
629
+ const b = other.data[i];
630
+ dotProduct += a * b;
631
+ normA += a * a;
632
+ normB += b * b;
633
+ }
634
+ const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
635
+ if (magnitude === 0)
636
+ return 0;
637
+ return dotProduct / magnitude;
638
+ };
639
+ /** Calculate dot product between two vectors */
640
+ const mDot = (receiver, args, _ctx, location) => {
641
+ if (!isVector(receiver)) {
642
+ throw new RuntimeError('RILL-R003', `dot requires vector receiver, got ${inferType(receiver)}`, location);
643
+ }
644
+ const other = args[0] ?? null;
645
+ if (!isVector(other)) {
646
+ throw new RuntimeError('RILL-R003', `expected vector, got ${inferType(other)}`, location);
647
+ }
648
+ if (receiver.data.length !== other.data.length) {
649
+ throw new RuntimeError('RILL-R003', `vector dimension mismatch: ${receiver.data.length} vs ${other.data.length}`, location);
650
+ }
651
+ let result = 0;
652
+ for (let i = 0; i < receiver.data.length; i++) {
653
+ result += receiver.data[i] * other.data[i];
654
+ }
655
+ return result;
656
+ };
657
+ /** Calculate Euclidean distance between two vectors (>= 0) */
658
+ const mDistance = (receiver, args, _ctx, location) => {
659
+ if (!isVector(receiver)) {
660
+ throw new RuntimeError('RILL-R003', `distance requires vector receiver, got ${inferType(receiver)}`, location);
661
+ }
662
+ const other = args[0] ?? null;
663
+ if (!isVector(other)) {
664
+ throw new RuntimeError('RILL-R003', `expected vector, got ${inferType(other)}`, location);
665
+ }
666
+ if (receiver.data.length !== other.data.length) {
667
+ throw new RuntimeError('RILL-R003', `vector dimension mismatch: ${receiver.data.length} vs ${other.data.length}`, location);
668
+ }
669
+ let sumSquares = 0;
670
+ for (let i = 0; i < receiver.data.length; i++) {
671
+ const diff = receiver.data[i] - other.data[i];
672
+ sumSquares += diff * diff;
673
+ }
674
+ return Math.sqrt(sumSquares);
675
+ };
676
+ /** Calculate L2 norm (magnitude) of vector */
677
+ const mNorm = (receiver, _args, _ctx, location) => {
678
+ if (!isVector(receiver)) {
679
+ throw new RuntimeError('RILL-R003', `norm requires vector receiver, got ${inferType(receiver)}`, location);
680
+ }
681
+ let sumSquares = 0;
682
+ for (let i = 0; i < receiver.data.length; i++) {
683
+ const val = receiver.data[i];
684
+ sumSquares += val * val;
685
+ }
686
+ return Math.sqrt(sumSquares);
687
+ };
688
+ /** Create unit vector (preserves model) */
689
+ const mNormalize = (receiver, _args, _ctx, location) => {
690
+ if (!isVector(receiver)) {
691
+ throw new RuntimeError('RILL-R003', `normalize requires vector receiver, got ${inferType(receiver)}`, location);
692
+ }
693
+ let sumSquares = 0;
694
+ for (let i = 0; i < receiver.data.length; i++) {
695
+ const val = receiver.data[i];
696
+ sumSquares += val * val;
697
+ }
698
+ const magnitude = Math.sqrt(sumSquares);
699
+ if (magnitude === 0)
700
+ return receiver;
701
+ const normalized = new Float32Array(receiver.data.length);
702
+ for (let i = 0; i < receiver.data.length; i++) {
703
+ normalized[i] = receiver.data[i] / magnitude;
704
+ }
705
+ return {
706
+ __rill_vector: true,
707
+ data: normalized,
708
+ model: receiver.model,
709
+ };
710
+ };
711
+ // ============================================================
712
+ // PER-TYPE METHOD RECORDS
713
+ // Populate BUILTIN_METHODS sub-records using buildMethodEntry.
714
+ // Methods shared across types reference the same RillMethod body.
715
+ // Cross-type methods (len, empty, eq, ne, head, tail, first, at,
716
+ // lt, gt, le, ge) appear in every type group they support.
717
+ // Vector methods live in the `vector` group (6th group beyond
718
+ // the 5 basic types) because no basic type covers vectors.
719
+ // ============================================================
720
+ // Shared signatures for cross-type methods
721
+ const SIG_LEN = '||:number';
722
+ const SIG_EMPTY = '||:bool';
723
+ const SIG_HEAD = '||:any';
724
+ const SIG_TAIL = '||:any';
725
+ const SIG_FIRST = '||:iterator';
726
+ const SIG_AT = '|index: number|:any';
727
+ const SIG_EQ = '|other: any|:bool';
728
+ const SIG_NE = '|other: any|:bool';
729
+ const SIG_CMP = '|other: any|:bool';
730
+ BUILTIN_METHODS.string = Object.freeze({
731
+ len: buildMethodEntry('len', SIG_LEN, mLen),
732
+ trim: buildMethodEntry('trim', '||:string', mTrim),
733
+ head: buildMethodEntry('head', SIG_HEAD, mHead),
734
+ tail: buildMethodEntry('tail', SIG_TAIL, mTail),
735
+ first: buildMethodEntry('first', SIG_FIRST, mFirst),
736
+ at: buildMethodEntry('at', SIG_AT, mAt),
737
+ split: buildMethodEntry('split', '|separator: string = "\\n"|:list', mSplit),
738
+ lines: buildMethodEntry('lines', '||:list', mLines),
739
+ empty: buildMethodEntry('empty', SIG_EMPTY, mEmpty),
740
+ starts_with: buildMethodEntry('starts_with', '|prefix: string|:bool', mStartsWith),
741
+ ends_with: buildMethodEntry('ends_with', '|suffix: string|:bool', mEndsWith),
742
+ lower: buildMethodEntry('lower', '||:string', mLower),
743
+ upper: buildMethodEntry('upper', '||:string', mUpper),
744
+ replace: buildMethodEntry('replace', '|pattern: string, replacement: string|:string', mReplace),
745
+ replace_all: buildMethodEntry('replace_all', '|pattern: string, replacement: string|:string', mReplaceAll),
746
+ contains: buildMethodEntry('contains', '|search: string|:bool', mContains),
747
+ match: buildMethodEntry('match', '|pattern: string|:dict', mMatch),
748
+ is_match: buildMethodEntry('is_match', '|pattern: string|:bool', mIsMatch),
749
+ index_of: buildMethodEntry('index_of', '|search: string|:number', mIndexOf),
750
+ repeat: buildMethodEntry('repeat', '|count: number|:string', mRepeat),
751
+ pad_start: buildMethodEntry('pad_start', '|length: number, fill: string = " "|:string', mPadStart),
752
+ pad_end: buildMethodEntry('pad_end', '|length: number, fill: string = " "|:string', mPadEnd),
753
+ eq: buildMethodEntry('eq', SIG_EQ, mEq),
754
+ ne: buildMethodEntry('ne', SIG_NE, mNe),
755
+ lt: buildMethodEntry('lt', SIG_CMP, mLt),
756
+ gt: buildMethodEntry('gt', SIG_CMP, mGt),
757
+ le: buildMethodEntry('le', SIG_CMP, mLe),
758
+ ge: buildMethodEntry('ge', SIG_CMP, mGe),
759
+ });
760
+ BUILTIN_METHODS.list = Object.freeze({
761
+ len: buildMethodEntry('len', SIG_LEN, mLen),
762
+ head: buildMethodEntry('head', SIG_HEAD, mHead),
763
+ tail: buildMethodEntry('tail', SIG_TAIL, mTail),
764
+ first: buildMethodEntry('first', SIG_FIRST, mFirst),
765
+ at: buildMethodEntry('at', SIG_AT, mAt),
766
+ join: buildMethodEntry('join', '|separator: string = ","|:string', mJoin),
767
+ empty: buildMethodEntry('empty', SIG_EMPTY, mEmpty),
768
+ eq: buildMethodEntry('eq', SIG_EQ, mEq),
769
+ ne: buildMethodEntry('ne', SIG_NE, mNe),
770
+ has: buildMethodEntry('has', '|value: any|:bool', mHas),
771
+ has_any: buildMethodEntry('has_any', '|candidates: list|:bool', mHasAny),
772
+ has_all: buildMethodEntry('has_all', '|candidates: list|:bool', mHasAll),
773
+ });
774
+ BUILTIN_METHODS.dict = Object.freeze({
775
+ len: buildMethodEntry('len', SIG_LEN, mLen),
776
+ first: buildMethodEntry('first', SIG_FIRST, mFirst),
777
+ empty: buildMethodEntry('empty', SIG_EMPTY, mEmpty),
778
+ eq: buildMethodEntry('eq', SIG_EQ, mEq),
779
+ ne: buildMethodEntry('ne', SIG_NE, mNe),
780
+ keys: buildMethodEntry('keys', '||:list', mKeys),
781
+ values: buildMethodEntry('values', '||:list', mValues),
782
+ entries: buildMethodEntry('entries', '||:list', mEntries),
783
+ });
784
+ BUILTIN_METHODS.number = Object.freeze({
785
+ empty: buildMethodEntry('empty', SIG_EMPTY, mEmpty),
786
+ eq: buildMethodEntry('eq', SIG_EQ, mEq),
787
+ ne: buildMethodEntry('ne', SIG_NE, mNe),
788
+ lt: buildMethodEntry('lt', SIG_CMP, mLt),
789
+ gt: buildMethodEntry('gt', SIG_CMP, mGt),
790
+ le: buildMethodEntry('le', SIG_CMP, mLe),
791
+ ge: buildMethodEntry('ge', SIG_CMP, mGe),
792
+ });
793
+ BUILTIN_METHODS.bool = Object.freeze({
794
+ empty: buildMethodEntry('empty', SIG_EMPTY, mEmpty),
795
+ eq: buildMethodEntry('eq', SIG_EQ, mEq),
796
+ ne: buildMethodEntry('ne', SIG_NE, mNe),
797
+ });
798
+ // [ASSUMPTION] vector is a 6th group beyond the 5 specified basic types.
799
+ // The 7 vector methods do not belong to string/list/dict/number/bool.
800
+ // Adding this group ensures all 42 methods are accessible (AC-36).
801
+ BUILTIN_METHODS.vector = Object.freeze({
802
+ dimensions: buildMethodEntry('dimensions', '||:number', mDimensions),
803
+ model: buildMethodEntry('model', '||:string', mModel),
804
+ similarity: buildMethodEntry('similarity', '|other: any|:number', mSimilarity),
805
+ dot: buildMethodEntry('dot', '|other: any|:number', mDot),
806
+ distance: buildMethodEntry('distance', '|other: any|:number', mDistance),
807
+ norm: buildMethodEntry('norm', '||:number', mNorm),
808
+ normalize: buildMethodEntry('normalize', '||:any', mNormalize),
809
+ });
840
810
  //# sourceMappingURL=builtins.js.map