tova 0.9.14 → 0.10.1
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/package.json +1 -1
- package/src/analyzer/analyzer.js +10 -1
- package/src/analyzer/type-registry.js +164 -0
- package/src/lsp/server.js +37 -2
- package/src/stdlib/inline.js +253 -9
- package/src/version.js +1 -1
package/package.json
CHANGED
package/src/analyzer/analyzer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Scope, Symbol } from './scope.js';
|
|
2
2
|
import { PIPE_TARGET } from '../parser/ast.js';
|
|
3
|
-
import { BUILTIN_NAMES } from '../stdlib/inline.js';
|
|
3
|
+
import { BUILTIN_NAMES, DEPRECATED_NAMES, SNAKE_TO_CAMEL } from '../stdlib/inline.js';
|
|
4
4
|
import { BlockRegistry } from '../registry/register-all.js';
|
|
5
5
|
import {
|
|
6
6
|
Type, PrimitiveType, NilType, AnyType, UnknownType,
|
|
@@ -854,6 +854,15 @@ export class Analyzer {
|
|
|
854
854
|
this.visitExpression(node.collection);
|
|
855
855
|
return;
|
|
856
856
|
case 'CallExpression':
|
|
857
|
+
// W_DEPRECATED_STDLIB: warn on snake_case stdlib function usage
|
|
858
|
+
if (node.callee.type === 'Identifier' && DEPRECATED_NAMES.has(node.callee.name)) {
|
|
859
|
+
this.warn(
|
|
860
|
+
`'${node.callee.name}' is deprecated, use '${SNAKE_TO_CAMEL[node.callee.name]}' instead`,
|
|
861
|
+
node.callee.loc || node.loc,
|
|
862
|
+
`rename to ${SNAKE_TO_CAMEL[node.callee.name]}`,
|
|
863
|
+
{ code: 'W_DEPRECATED_STDLIB' }
|
|
864
|
+
);
|
|
865
|
+
}
|
|
857
866
|
// W_DANGEROUS_API: detect setTimeout/setInterval with string args
|
|
858
867
|
if (node.callee.type === 'Identifier') {
|
|
859
868
|
const calleeName = node.callee.name;
|
|
@@ -88,4 +88,168 @@ export class TypeRegistry {
|
|
|
88
88
|
}
|
|
89
89
|
return [];
|
|
90
90
|
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Extract the base type name from a composite type string.
|
|
94
|
+
* "[Int]" → "Array", "Result<Int, String>" → "Result", "Option<String>" → "Option", "String" → "String"
|
|
95
|
+
*/
|
|
96
|
+
static extractBaseType(typeName) {
|
|
97
|
+
if (!typeName) return null;
|
|
98
|
+
if (typeName.startsWith('[') && typeName.endsWith(']')) return 'Array';
|
|
99
|
+
if (typeName.startsWith('Result<') || typeName === 'Result') return 'Result';
|
|
100
|
+
if (typeName.startsWith('Option<') || typeName === 'Option') return 'Option';
|
|
101
|
+
if (typeName.startsWith('Map<') || typeName === 'Map') return 'Map';
|
|
102
|
+
if (typeName.startsWith('Set<') || typeName === 'Set') return 'Set';
|
|
103
|
+
return typeName;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get built-in members (fields + methods) for a built-in type.
|
|
108
|
+
* Returns { fields: Map, methods: [] } or null if not a built-in type.
|
|
109
|
+
*/
|
|
110
|
+
getBuiltinMembers(typeName) {
|
|
111
|
+
const base = TypeRegistry.extractBaseType(typeName);
|
|
112
|
+
const descriptors = TypeRegistry.BUILTIN_MEMBERS[base];
|
|
113
|
+
if (!descriptors) return null;
|
|
114
|
+
|
|
115
|
+
const fields = new Map();
|
|
116
|
+
const methods = [];
|
|
117
|
+
for (const d of descriptors) {
|
|
118
|
+
if (d.kind === 'field') {
|
|
119
|
+
fields.set(d.name, d.returnType);
|
|
120
|
+
} else {
|
|
121
|
+
methods.push(d);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return { fields, methods };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ─── Built-in type member descriptors ──────────────────────────
|
|
129
|
+
|
|
130
|
+
function field(name, returnType, doc) {
|
|
131
|
+
return { kind: 'field', name, returnType, doc };
|
|
91
132
|
}
|
|
133
|
+
|
|
134
|
+
function method(name, params, returnType, doc) {
|
|
135
|
+
return { kind: 'method', name, params, returnType, doc };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
TypeRegistry.BUILTIN_MEMBERS = {
|
|
139
|
+
String: [
|
|
140
|
+
field('length', 'Int', 'Number of characters'),
|
|
141
|
+
method('slice', ['start: Int', 'end?: Int'], 'String', 'Extract a section of the string'),
|
|
142
|
+
method('includes', ['search: String'], 'Bool', 'Check if string contains substring'),
|
|
143
|
+
method('indexOf', ['search: String'], 'Int', 'First index of substring, or -1'),
|
|
144
|
+
method('lastIndexOf', ['search: String'], 'Int', 'Last index of substring, or -1'),
|
|
145
|
+
method('startsWith', ['prefix: String'], 'Bool', 'Check if string starts with prefix'),
|
|
146
|
+
method('endsWith', ['suffix: String'], 'Bool', 'Check if string ends with suffix'),
|
|
147
|
+
method('trim', [], 'String', 'Remove whitespace from both ends'),
|
|
148
|
+
method('trimStart', [], 'String', 'Remove whitespace from start'),
|
|
149
|
+
method('trimEnd', [], 'String', 'Remove whitespace from end'),
|
|
150
|
+
method('toUpperCase', [], 'String', 'Convert to uppercase'),
|
|
151
|
+
method('toLowerCase', [], 'String', 'Convert to lowercase'),
|
|
152
|
+
method('split', ['separator: String'], '[String]', 'Split into array of substrings'),
|
|
153
|
+
method('replace', ['search: String', 'replacement: String'], 'String', 'Replace first occurrence'),
|
|
154
|
+
method('replaceAll', ['search: String', 'replacement: String'], 'String', 'Replace all occurrences'),
|
|
155
|
+
method('repeat', ['count: Int'], 'String', 'Repeat the string n times'),
|
|
156
|
+
method('charAt', ['index: Int'], 'String', 'Character at index'),
|
|
157
|
+
method('charCodeAt', ['index: Int'], 'Int', 'Character code at index'),
|
|
158
|
+
method('concat', ['other: String'], 'String', 'Concatenate strings'),
|
|
159
|
+
method('padStart', ['length: Int', 'fill?: String'], 'String', 'Pad from start to length'),
|
|
160
|
+
method('padEnd', ['length: Int', 'fill?: String'], 'String', 'Pad from end to length'),
|
|
161
|
+
method('match', ['pattern: String'], 'Option', 'Match against regex pattern'),
|
|
162
|
+
method('search', ['pattern: String'], 'Int', 'Search for regex pattern'),
|
|
163
|
+
method('toString', [], 'String', 'Convert to string'),
|
|
164
|
+
method('substring', ['start: Int', 'end?: Int'], 'String', 'Extract characters between indices'),
|
|
165
|
+
method('at', ['index: Int'], 'String', 'Character at index (supports negative)'),
|
|
166
|
+
],
|
|
167
|
+
Array: [
|
|
168
|
+
field('length', 'Int', 'Number of elements'),
|
|
169
|
+
method('push', ['item: T'], 'Int', 'Add element to end, returns new length'),
|
|
170
|
+
method('pop', [], 'T', 'Remove and return last element'),
|
|
171
|
+
method('shift', [], 'T', 'Remove and return first element'),
|
|
172
|
+
method('unshift', ['item: T'], 'Int', 'Add element to start, returns new length'),
|
|
173
|
+
method('splice', ['start: Int', 'deleteCount?: Int'], '[T]', 'Remove/replace elements'),
|
|
174
|
+
method('slice', ['start?: Int', 'end?: Int'], '[T]', 'Extract a section of the array'),
|
|
175
|
+
method('concat', ['other: [T]'], '[T]', 'Merge arrays'),
|
|
176
|
+
method('join', ['separator?: String'], 'String', 'Join elements into string'),
|
|
177
|
+
method('reverse', [], '[T]', 'Reverse array in place'),
|
|
178
|
+
method('sort', ['compareFn?: fn(T, T) -> Int'], '[T]', 'Sort array in place'),
|
|
179
|
+
method('map', ['fn: fn(T) -> U'], '[U]', 'Transform each element'),
|
|
180
|
+
method('filter', ['fn: fn(T) -> Bool'], '[T]', 'Keep elements matching predicate'),
|
|
181
|
+
method('reduce', ['fn: fn(acc, T) -> U', 'initial: U'], 'U', 'Reduce to single value'),
|
|
182
|
+
method('find', ['fn: fn(T) -> Bool'], 'Option', 'Find first matching element'),
|
|
183
|
+
method('findIndex', ['fn: fn(T) -> Bool'], 'Int', 'Index of first match, or -1'),
|
|
184
|
+
method('some', ['fn: fn(T) -> Bool'], 'Bool', 'Check if any element matches'),
|
|
185
|
+
method('every', ['fn: fn(T) -> Bool'], 'Bool', 'Check if all elements match'),
|
|
186
|
+
method('includes', ['item: T'], 'Bool', 'Check if array contains element'),
|
|
187
|
+
method('indexOf', ['item: T'], 'Int', 'First index of element, or -1'),
|
|
188
|
+
method('flat', [], '[T]', 'Flatten one level of nesting'),
|
|
189
|
+
method('flatMap', ['fn: fn(T) -> [U]'], '[U]', 'Map then flatten'),
|
|
190
|
+
method('fill', ['value: T', 'start?: Int', 'end?: Int'], '[T]', 'Fill with value'),
|
|
191
|
+
method('forEach', ['fn: fn(T) -> Nil'], 'Nil', 'Execute function for each element'),
|
|
192
|
+
method('at', ['index: Int'], 'T', 'Element at index (supports negative)'),
|
|
193
|
+
],
|
|
194
|
+
Result: [
|
|
195
|
+
method('map', ['fn: fn(T) -> U'], 'Result', 'Transform Ok value'),
|
|
196
|
+
method('flatMap', ['fn: fn(T) -> Result'], 'Result', 'Chain Result-returning function'),
|
|
197
|
+
method('andThen', ['fn: fn(T) -> Result'], 'Result', 'Alias for flatMap'),
|
|
198
|
+
method('unwrap', [], 'T', 'Get Ok value or throw on Err'),
|
|
199
|
+
method('unwrapOr', ['default: T'], 'T', 'Get Ok value or return default'),
|
|
200
|
+
method('expect', ['msg: String'], 'T', 'Get Ok value or throw with message'),
|
|
201
|
+
method('isOk', [], 'Bool', 'Check if Result is Ok'),
|
|
202
|
+
method('isErr', [], 'Bool', 'Check if Result is Err'),
|
|
203
|
+
method('mapErr', ['fn: fn(E) -> F'], 'Result', 'Transform Err value'),
|
|
204
|
+
method('unwrapErr', [], 'E', 'Get Err value or throw on Ok'),
|
|
205
|
+
method('or', ['other: Result'], 'Result', 'Return self if Ok, otherwise other'),
|
|
206
|
+
method('and', ['other: Result'], 'Result', 'Return other if Ok, otherwise self'),
|
|
207
|
+
method('context', ['msg: String'], 'Result', 'Add context to Err'),
|
|
208
|
+
],
|
|
209
|
+
Option: [
|
|
210
|
+
method('map', ['fn: fn(T) -> U'], 'Option', 'Transform Some value'),
|
|
211
|
+
method('flatMap', ['fn: fn(T) -> Option'], 'Option', 'Chain Option-returning function'),
|
|
212
|
+
method('andThen', ['fn: fn(T) -> Option'], 'Option', 'Alias for flatMap'),
|
|
213
|
+
method('unwrap', [], 'T', 'Get Some value or throw on None'),
|
|
214
|
+
method('unwrapOr', ['default: T'], 'T', 'Get Some value or return default'),
|
|
215
|
+
method('expect', ['msg: String'], 'T', 'Get Some value or throw with message'),
|
|
216
|
+
method('isSome', [], 'Bool', 'Check if Option is Some'),
|
|
217
|
+
method('isNone', [], 'Bool', 'Check if Option is None'),
|
|
218
|
+
method('or', ['other: Option'], 'Option', 'Return self if Some, otherwise other'),
|
|
219
|
+
method('and', ['other: Option'], 'Option', 'Return other if Some, otherwise self'),
|
|
220
|
+
method('filter', ['fn: fn(T) -> Bool'], 'Option', 'Keep Some if predicate matches'),
|
|
221
|
+
],
|
|
222
|
+
Map: [
|
|
223
|
+
field('size', 'Int', 'Number of key-value pairs'),
|
|
224
|
+
method('get', ['key: K'], 'V', 'Get value by key'),
|
|
225
|
+
method('set', ['key: K', 'value: V'], 'Map', 'Set key-value pair'),
|
|
226
|
+
method('has', ['key: K'], 'Bool', 'Check if key exists'),
|
|
227
|
+
method('delete', ['key: K'], 'Bool', 'Remove key-value pair'),
|
|
228
|
+
method('clear', [], 'Nil', 'Remove all entries'),
|
|
229
|
+
method('keys', [], '[K]', 'Get all keys'),
|
|
230
|
+
method('values', [], '[V]', 'Get all values'),
|
|
231
|
+
method('entries', [], '[(K, V)]', 'Get all key-value pairs'),
|
|
232
|
+
method('forEach', ['fn: fn(V, K) -> Nil'], 'Nil', 'Execute function for each entry'),
|
|
233
|
+
],
|
|
234
|
+
Set: [
|
|
235
|
+
field('size', 'Int', 'Number of elements'),
|
|
236
|
+
method('add', ['value: T'], 'Set', 'Add element'),
|
|
237
|
+
method('has', ['value: T'], 'Bool', 'Check if element exists'),
|
|
238
|
+
method('delete', ['value: T'], 'Bool', 'Remove element'),
|
|
239
|
+
method('clear', [], 'Nil', 'Remove all elements'),
|
|
240
|
+
method('keys', [], '[T]', 'Get all values (alias)'),
|
|
241
|
+
method('values', [], '[T]', 'Get all values'),
|
|
242
|
+
method('entries', [], '[(T, T)]', 'Get all value-value pairs'),
|
|
243
|
+
method('forEach', ['fn: fn(T) -> Nil'], 'Nil', 'Execute function for each element'),
|
|
244
|
+
],
|
|
245
|
+
Int: [
|
|
246
|
+
method('toString', [], 'String', 'Convert to string'),
|
|
247
|
+
method('toFixed', ['digits?: Int'], 'String', 'Format with fixed decimal places'),
|
|
248
|
+
method('toPrecision', ['precision?: Int'], 'String', 'Format to specified precision'),
|
|
249
|
+
],
|
|
250
|
+
Float: [
|
|
251
|
+
method('toString', [], 'String', 'Convert to string'),
|
|
252
|
+
method('toFixed', ['digits?: Int'], 'String', 'Format with fixed decimal places'),
|
|
253
|
+
method('toPrecision', ['precision?: Int'], 'String', 'Format to specified precision'),
|
|
254
|
+
],
|
|
255
|
+
};
|
package/src/lsp/server.js
CHANGED
|
@@ -8,7 +8,7 @@ import { Analyzer } from '../analyzer/analyzer.js';
|
|
|
8
8
|
import { TokenType } from '../lexer/tokens.js';
|
|
9
9
|
import { Formatter } from '../formatter/formatter.js';
|
|
10
10
|
import { TypeRegistry } from '../analyzer/type-registry.js';
|
|
11
|
-
import { BUILTIN_NAMES, BUILTIN_FUNCTIONS } from '../stdlib/inline.js';
|
|
11
|
+
import { BUILTIN_NAMES, BUILTIN_FUNCTIONS, DEPRECATED_NAMES, SNAKE_TO_CAMEL } from '../stdlib/inline.js';
|
|
12
12
|
|
|
13
13
|
class TovaLanguageServer {
|
|
14
14
|
static MAX_CACHE_SIZE = 100; // max cached diagnostics entries
|
|
@@ -532,7 +532,12 @@ class TovaLanguageServer {
|
|
|
532
532
|
for (const fn of BUILTIN_NAMES) {
|
|
533
533
|
if (fn.startsWith(prefix) && !fn.startsWith('__')) {
|
|
534
534
|
const detail = this._getBuiltinDetail(fn);
|
|
535
|
-
|
|
535
|
+
const item = { label: fn, kind: 3 /* Function */, detail };
|
|
536
|
+
if (DEPRECATED_NAMES.has(fn)) {
|
|
537
|
+
item.tags = [1]; // CompletionItemTag.Deprecated
|
|
538
|
+
item.detail = `(deprecated) use ${SNAKE_TO_CAMEL[fn]} → ${detail}`;
|
|
539
|
+
}
|
|
540
|
+
items.push(item);
|
|
536
541
|
}
|
|
537
542
|
}
|
|
538
543
|
// Runtime types
|
|
@@ -629,6 +634,36 @@ class TovaLanguageServer {
|
|
|
629
634
|
});
|
|
630
635
|
}
|
|
631
636
|
}
|
|
637
|
+
|
|
638
|
+
// Fall back to built-in type members if no user-defined members found
|
|
639
|
+
if (items.length === 0) {
|
|
640
|
+
const builtin = typeRegistry.getBuiltinMembers(typeName);
|
|
641
|
+
if (builtin) {
|
|
642
|
+
for (const [fieldName, fieldType] of builtin.fields) {
|
|
643
|
+
if (!partial || fieldName.startsWith(partial)) {
|
|
644
|
+
items.push({
|
|
645
|
+
label: fieldName,
|
|
646
|
+
kind: 5, // Field
|
|
647
|
+
detail: fieldType || 'field',
|
|
648
|
+
sortText: `0${fieldName}`,
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
for (const m of builtin.methods) {
|
|
653
|
+
if (!partial || m.name.startsWith(partial)) {
|
|
654
|
+
const paramStr = (m.params || []).join(', ');
|
|
655
|
+
const retStr = m.returnType ? ` -> ${m.returnType}` : '';
|
|
656
|
+
items.push({
|
|
657
|
+
label: m.name,
|
|
658
|
+
kind: 2, // Method
|
|
659
|
+
detail: `fn(${paramStr})${retStr}`,
|
|
660
|
+
documentation: m.doc,
|
|
661
|
+
sortText: `1${m.name}`,
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
632
667
|
}
|
|
633
668
|
}
|
|
634
669
|
|
package/src/stdlib/inline.js
CHANGED
|
@@ -1618,6 +1618,238 @@ function __chart_empty(w, h, msg) { return '<svg xmlns="http://www.w3.org/2000/s
|
|
|
1618
1618
|
heatmap: `function heatmap(data, opts) { if (!opts) opts = {}; var rows = __chart_getRows(data); var width = opts.width || 600; var height = opts.height || 400; if (rows.length === 0) return __chart_empty(width, height, 'No data'); var xFn = opts.x; var yFn = opts.y; var valueFn = opts.value; var title = opts.title || ''; var margin = { top: title ? 50 : 40, right: 40, bottom: 60, left: 80 }; var xCats = []; var yCats = []; var xSet = new Set(); var ySet = new Set(); for (var i = 0; i < rows.length; i++) { var xv = String(xFn(rows[i])); var yv = String(yFn(rows[i])); if (!xSet.has(xv)) { xSet.add(xv); xCats.push(xv); } if (!ySet.has(yv)) { ySet.add(yv); yCats.push(yv); } } var grid = {}; var vMn = Infinity; var vMx = -Infinity; for (var i = 0; i < rows.length; i++) { var xv = String(xFn(rows[i])); var yv = String(yFn(rows[i])); var val = Number(valueFn(rows[i])); grid[xv + '|' + yv] = val; if (val < vMn) vMn = val; if (val > vMx) vMx = val; } var vR = vMx - vMn || 1; var plotW = width - margin.left - margin.right; var plotH = height - margin.top - margin.bottom; var cellW = plotW / xCats.length; var cellH = plotH / yCats.length; function hc(val) { var t = (val - vMn) / vR; var r = Math.round(255 - t * (255 - 79)); var g = Math.round(255 - t * (255 - 70)); var b = Math.round(255 - t * (255 - 229)); return 'rgb(' + r + ',' + g + ',' + b + ')'; } var p = []; p.push('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + width + ' ' + height + '" width="' + width + '" height="' + height + '" style="font-family:system-ui,sans-serif">'); if (title) p.push('<text x="' + (width / 2) + '" y="24" text-anchor="middle" font-size="16" font-weight="bold" fill="#111">' + __chart_esc(title) + '</text>'); for (var xi = 0; xi < xCats.length; xi++) { for (var yi = 0; yi < yCats.length; yi++) { var key = xCats[xi] + '|' + yCats[yi]; var val = grid[key]; var rx = margin.left + xi * cellW; var ry = margin.top + yi * cellH; var fill = val !== undefined ? hc(val) : '#f3f4f6'; p.push('<rect x="' + rx + '" y="' + ry + '" width="' + cellW + '" height="' + cellH + '" fill="' + fill + '" stroke="#fff" stroke-width="1"/>'); if (val !== undefined) { var tc = ((val - vMn) / vR) > 0.5 ? '#fff' : '#111'; p.push('<text x="' + (rx + cellW / 2) + '" y="' + (ry + cellH / 2 + 4) + '" text-anchor="middle" font-size="11" fill="' + tc + '">' + __chart_formatNum(val) + '</text>'); } } } for (var xi = 0; xi < xCats.length; xi++) { var lx = margin.left + xi * cellW + cellW / 2; var ly = margin.top + plotH + 16; p.push('<text x="' + lx + '" y="' + ly + '" text-anchor="middle" font-size="11" fill="#666">' + __chart_esc(xCats[xi]) + '</text>'); } for (var yi = 0; yi < yCats.length; yi++) { var lx = margin.left - 8; var ly = margin.top + yi * cellH + cellH / 2 + 4; p.push('<text x="' + lx + '" y="' + ly + '" text-anchor="end" font-size="11" fill="#666">' + __chart_esc(yCats[yi]) + '</text>'); } p.push('</svg>'); return p.join('\\n'); }`,
|
|
1619
1619
|
};
|
|
1620
1620
|
|
|
1621
|
+
// ─── Snake_case → camelCase Migration ──────────────────────────
|
|
1622
|
+
// Maps deprecated snake_case stdlib names to their camelCase replacements.
|
|
1623
|
+
// Both names work; old names emit a deprecation warning via the analyzer.
|
|
1624
|
+
export const SNAKE_TO_CAMEL = {
|
|
1625
|
+
// Collections / iterators
|
|
1626
|
+
type_of: 'typeOf',
|
|
1627
|
+
flat_map: 'flatMap',
|
|
1628
|
+
group_by: 'groupBy',
|
|
1629
|
+
sort_by: 'sortBy',
|
|
1630
|
+
find_index: 'findIndex',
|
|
1631
|
+
min_by: 'minBy',
|
|
1632
|
+
max_by: 'maxBy',
|
|
1633
|
+
sum_by: 'sumBy',
|
|
1634
|
+
zip_with: 'zipWith',
|
|
1635
|
+
sliding_window: 'slidingWindow',
|
|
1636
|
+
binary_search: 'binarySearch',
|
|
1637
|
+
is_sorted: 'isSorted',
|
|
1638
|
+
insert_at: 'insertAt',
|
|
1639
|
+
remove_at: 'removeAt',
|
|
1640
|
+
update_at: 'updateAt',
|
|
1641
|
+
repeat_value: 'repeatValue',
|
|
1642
|
+
from_entries: 'fromEntries',
|
|
1643
|
+
has_key: 'hasKey',
|
|
1644
|
+
map_values: 'mapValues',
|
|
1645
|
+
drop_duplicates: 'dropDuplicates',
|
|
1646
|
+
drop_nil: 'dropNil',
|
|
1647
|
+
fill_nil: 'fillNil',
|
|
1648
|
+
filter_ok: 'filterOk',
|
|
1649
|
+
filter_err: 'filterErr',
|
|
1650
|
+
// Strings
|
|
1651
|
+
starts_with: 'startsWith',
|
|
1652
|
+
ends_with: 'endsWith',
|
|
1653
|
+
title_case: 'titleCase',
|
|
1654
|
+
snake_case: 'snakeCase',
|
|
1655
|
+
camel_case: 'camelCase',
|
|
1656
|
+
kebab_case: 'kebabCase',
|
|
1657
|
+
replace_first: 'replaceFirst',
|
|
1658
|
+
pad_start: 'padStart',
|
|
1659
|
+
pad_end: 'padEnd',
|
|
1660
|
+
char_at: 'charAt',
|
|
1661
|
+
trim_start: 'trimStart',
|
|
1662
|
+
trim_end: 'trimEnd',
|
|
1663
|
+
index_of: 'indexOf',
|
|
1664
|
+
last_index_of: 'lastIndexOf',
|
|
1665
|
+
count_of: 'countOf',
|
|
1666
|
+
reverse_str: 'reverseStr',
|
|
1667
|
+
indent_str: 'indentStr',
|
|
1668
|
+
escape_html: 'escapeHtml',
|
|
1669
|
+
unescape_html: 'unescapeHtml',
|
|
1670
|
+
word_wrap: 'wordWrap',
|
|
1671
|
+
is_empty: 'isEmpty',
|
|
1672
|
+
// Conversions
|
|
1673
|
+
to_int: 'toInt',
|
|
1674
|
+
to_float: 'toFloat',
|
|
1675
|
+
to_string: 'toString',
|
|
1676
|
+
to_bool: 'toBool',
|
|
1677
|
+
to_hex: 'toHex',
|
|
1678
|
+
to_binary: 'toBinary',
|
|
1679
|
+
to_octal: 'toOctal',
|
|
1680
|
+
to_fixed: 'toFixed',
|
|
1681
|
+
to_radians: 'toRadians',
|
|
1682
|
+
to_degrees: 'toDegrees',
|
|
1683
|
+
// Math
|
|
1684
|
+
is_nan: 'isNaN',
|
|
1685
|
+
is_finite: 'isFinite',
|
|
1686
|
+
is_close: 'isClose',
|
|
1687
|
+
random_int: 'randomInt',
|
|
1688
|
+
random_float: 'randomFloat',
|
|
1689
|
+
format_number: 'formatNumber',
|
|
1690
|
+
// Testing
|
|
1691
|
+
assert_eq: 'assertEq',
|
|
1692
|
+
assert_ne: 'assertNe',
|
|
1693
|
+
assert_throws: 'assertThrows',
|
|
1694
|
+
assert_snapshot: 'assertSnapshot',
|
|
1695
|
+
create_spy: 'createSpy',
|
|
1696
|
+
create_mock: 'createMock',
|
|
1697
|
+
// JSON / encoding
|
|
1698
|
+
json_parse: 'jsonParse',
|
|
1699
|
+
json_stringify: 'jsonStringify',
|
|
1700
|
+
json_pretty: 'jsonPretty',
|
|
1701
|
+
base64_encode: 'base64Encode',
|
|
1702
|
+
base64_decode: 'base64Decode',
|
|
1703
|
+
url_encode: 'urlEncode',
|
|
1704
|
+
url_decode: 'urlDecode',
|
|
1705
|
+
hex_encode: 'hexEncode',
|
|
1706
|
+
hex_decode: 'hexDecode',
|
|
1707
|
+
// URL
|
|
1708
|
+
parse_url: 'parseUrl',
|
|
1709
|
+
build_url: 'buildUrl',
|
|
1710
|
+
parse_query: 'parseQuery',
|
|
1711
|
+
build_query: 'buildQuery',
|
|
1712
|
+
// Date / time
|
|
1713
|
+
date_parse: 'dateParse',
|
|
1714
|
+
date_format: 'dateFormat',
|
|
1715
|
+
date_from: 'dateFrom',
|
|
1716
|
+
date_add: 'dateAdd',
|
|
1717
|
+
date_diff: 'dateDiff',
|
|
1718
|
+
date_part: 'datePart',
|
|
1719
|
+
now_iso: 'nowIso',
|
|
1720
|
+
time_ago: 'timeAgo',
|
|
1721
|
+
// Regex
|
|
1722
|
+
regex_test: 'regexTest',
|
|
1723
|
+
regex_match: 'regexMatch',
|
|
1724
|
+
regex_find_all: 'regexFindAll',
|
|
1725
|
+
regex_replace: 'regexReplace',
|
|
1726
|
+
regex_split: 'regexSplit',
|
|
1727
|
+
regex_capture: 'regexCapture',
|
|
1728
|
+
regex_builder: 'regexBuilder',
|
|
1729
|
+
// Validation
|
|
1730
|
+
is_email: 'isEmail',
|
|
1731
|
+
is_url: 'isUrl',
|
|
1732
|
+
is_numeric: 'isNumeric',
|
|
1733
|
+
is_alpha: 'isAlpha',
|
|
1734
|
+
is_alphanumeric: 'isAlphanumeric',
|
|
1735
|
+
is_uuid: 'isUuid',
|
|
1736
|
+
is_hex: 'isHex',
|
|
1737
|
+
// Sets
|
|
1738
|
+
is_subset: 'isSubset',
|
|
1739
|
+
is_superset: 'isSuperset',
|
|
1740
|
+
symmetric_difference: 'symmetricDifference',
|
|
1741
|
+
// Functional
|
|
1742
|
+
compare_by: 'compareBy',
|
|
1743
|
+
pipe_fn: 'pipeFn',
|
|
1744
|
+
try_fn: 'tryFn',
|
|
1745
|
+
try_async: 'tryAsync',
|
|
1746
|
+
schema_of: 'schemaOf',
|
|
1747
|
+
// File system
|
|
1748
|
+
is_file: 'isFile',
|
|
1749
|
+
is_dir: 'isDir',
|
|
1750
|
+
is_symlink: 'isSymlink',
|
|
1751
|
+
glob_files: 'globFiles',
|
|
1752
|
+
read_text: 'readText',
|
|
1753
|
+
read_bytes: 'readBytes',
|
|
1754
|
+
write_text: 'writeText',
|
|
1755
|
+
read_stdin: 'readStdin',
|
|
1756
|
+
read_lines: 'readLines',
|
|
1757
|
+
file_stat: 'fileStat',
|
|
1758
|
+
file_size: 'fileSize',
|
|
1759
|
+
// Path
|
|
1760
|
+
path_join: 'pathJoin',
|
|
1761
|
+
path_dirname: 'pathDirname',
|
|
1762
|
+
path_basename: 'pathBasename',
|
|
1763
|
+
path_resolve: 'pathResolve',
|
|
1764
|
+
path_ext: 'pathExt',
|
|
1765
|
+
path_relative: 'pathRelative',
|
|
1766
|
+
// Script
|
|
1767
|
+
script_path: 'scriptPath',
|
|
1768
|
+
script_dir: 'scriptDir',
|
|
1769
|
+
// Process
|
|
1770
|
+
set_env: 'setEnv',
|
|
1771
|
+
on_signal: 'onSignal',
|
|
1772
|
+
parse_args: 'parseArgs',
|
|
1773
|
+
// CLI
|
|
1774
|
+
choose_many: 'chooseMany',
|
|
1775
|
+
// Concurrency
|
|
1776
|
+
parallel_map: 'parallelMap',
|
|
1777
|
+
// Charts
|
|
1778
|
+
bar_chart: 'barChart',
|
|
1779
|
+
line_chart: 'lineChart',
|
|
1780
|
+
scatter_chart: 'scatterChart',
|
|
1781
|
+
pie_chart: 'pieChart',
|
|
1782
|
+
// Table operations
|
|
1783
|
+
table_where: 'tableWhere',
|
|
1784
|
+
table_select: 'tableSelect',
|
|
1785
|
+
table_derive: 'tableDerive',
|
|
1786
|
+
table_group_by: 'tableGroupBy',
|
|
1787
|
+
table_agg: 'tableAgg',
|
|
1788
|
+
table_sort_by: 'tableSortBy',
|
|
1789
|
+
table_limit: 'tableLimit',
|
|
1790
|
+
table_join: 'tableJoin',
|
|
1791
|
+
table_pivot: 'tablePivot',
|
|
1792
|
+
table_unpivot: 'tableUnpivot',
|
|
1793
|
+
table_explode: 'tableExplode',
|
|
1794
|
+
table_union: 'tableUnion',
|
|
1795
|
+
table_drop_duplicates: 'tableDropDuplicates',
|
|
1796
|
+
table_rename: 'tableRename',
|
|
1797
|
+
table_window: 'tableWindow',
|
|
1798
|
+
table_sample: 'tableSample',
|
|
1799
|
+
table_stratified_sample: 'tableStratifiedSample',
|
|
1800
|
+
// Aggregation functions
|
|
1801
|
+
agg_sum: 'aggSum',
|
|
1802
|
+
agg_count: 'aggCount',
|
|
1803
|
+
agg_mean: 'aggMean',
|
|
1804
|
+
agg_median: 'aggMedian',
|
|
1805
|
+
agg_min: 'aggMin',
|
|
1806
|
+
agg_max: 'aggMax',
|
|
1807
|
+
// Window functions
|
|
1808
|
+
win_row_number: 'winRowNumber',
|
|
1809
|
+
win_rank: 'winRank',
|
|
1810
|
+
win_dense_rank: 'winDenseRank',
|
|
1811
|
+
win_percent_rank: 'winPercentRank',
|
|
1812
|
+
win_ntile: 'winNtile',
|
|
1813
|
+
win_lag: 'winLag',
|
|
1814
|
+
win_lead: 'winLead',
|
|
1815
|
+
win_first_value: 'winFirstValue',
|
|
1816
|
+
win_last_value: 'winLastValue',
|
|
1817
|
+
win_running_sum: 'winRunningSum',
|
|
1818
|
+
win_running_count: 'winRunningCount',
|
|
1819
|
+
win_running_avg: 'winRunningAvg',
|
|
1820
|
+
win_running_min: 'winRunningMin',
|
|
1821
|
+
win_running_max: 'winRunningMax',
|
|
1822
|
+
win_moving_avg: 'winMovingAvg',
|
|
1823
|
+
// Typed arrays
|
|
1824
|
+
typed_sum: 'typedSum',
|
|
1825
|
+
typed_dot: 'typedDot',
|
|
1826
|
+
typed_add: 'typedAdd',
|
|
1827
|
+
typed_scale: 'typedScale',
|
|
1828
|
+
typed_map: 'typedMap',
|
|
1829
|
+
typed_reduce: 'typedReduce',
|
|
1830
|
+
typed_sort: 'typedSort',
|
|
1831
|
+
typed_zeros: 'typedZeros',
|
|
1832
|
+
typed_ones: 'typedOnes',
|
|
1833
|
+
typed_fill: 'typedFill',
|
|
1834
|
+
typed_linspace: 'typedLinspace',
|
|
1835
|
+
typed_norm: 'typedNorm',
|
|
1836
|
+
typed_range: 'typedRange',
|
|
1837
|
+
};
|
|
1838
|
+
|
|
1839
|
+
export const DEPRECATED_NAMES = new Set(Object.keys(SNAKE_TO_CAMEL));
|
|
1840
|
+
|
|
1841
|
+
// Reverse mapping: camelCase → snake_case (for tooling)
|
|
1842
|
+
export const CAMEL_TO_SNAKE = Object.fromEntries(
|
|
1843
|
+
Object.entries(SNAKE_TO_CAMEL).map(([s, c]) => [c, s])
|
|
1844
|
+
);
|
|
1845
|
+
|
|
1846
|
+
// Generate camelCase wrapper functions that delegate to snake_case originals
|
|
1847
|
+
for (const [snake, camel] of Object.entries(SNAKE_TO_CAMEL)) {
|
|
1848
|
+
if (BUILTIN_FUNCTIONS[snake] && !BUILTIN_FUNCTIONS[camel]) {
|
|
1849
|
+
BUILTIN_FUNCTIONS[camel] = `function ${camel}() { return ${snake}.apply(null, arguments); }`;
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1621
1853
|
// All known builtin names for matching
|
|
1622
1854
|
export const BUILTIN_NAMES = new Set(Object.keys(BUILTIN_FUNCTIONS));
|
|
1623
1855
|
|
|
@@ -1709,6 +1941,14 @@ export const STDLIB_DEPS = {
|
|
|
1709
1941
|
heatmap: ['Table', '__chart_helpers'],
|
|
1710
1942
|
};
|
|
1711
1943
|
|
|
1944
|
+
// Generate STDLIB_DEPS entries for camelCase wrappers
|
|
1945
|
+
// Each camelCase wrapper depends on its snake_case original (+ that original's deps)
|
|
1946
|
+
for (const [snake, camel] of Object.entries(SNAKE_TO_CAMEL)) {
|
|
1947
|
+
if (BUILTIN_FUNCTIONS[camel]) {
|
|
1948
|
+
STDLIB_DEPS[camel] = [snake, ...(STDLIB_DEPS[snake] || [])];
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1712
1952
|
// Resolve all transitive dependencies for a set of used names
|
|
1713
1953
|
export function resolveStdlibDeps(usedNames) {
|
|
1714
1954
|
const resolved = new Set(usedNames);
|
|
@@ -1757,16 +1997,20 @@ function _topoSort(names) {
|
|
|
1757
1997
|
// Only includes non-internal, non-table functions for backward compat with tests/playground
|
|
1758
1998
|
const _LEGACY_NAMES = [
|
|
1759
1999
|
'print', 'len', 'range', 'enumerate', 'sum', 'sorted', 'reversed', 'zip',
|
|
1760
|
-
'min', 'max', 'type_of', 'filter', 'map', 'find', 'any', 'all',
|
|
1761
|
-
'
|
|
1762
|
-
'
|
|
1763
|
-
'
|
|
2000
|
+
'min', 'max', 'type_of', 'typeOf', 'filter', 'map', 'find', 'any', 'all',
|
|
2001
|
+
'flat_map', 'flatMap', 'reduce', 'unique', 'group_by', 'groupBy',
|
|
2002
|
+
'chunk', 'flatten', 'take', 'drop', 'first', 'last', 'count', 'partition',
|
|
2003
|
+
'abs', 'floor', 'ceil', 'round', 'clamp', 'sqrt', 'pow', 'random',
|
|
2004
|
+
'trim', 'split', 'join', 'replace', 'repeat',
|
|
1764
2005
|
'keys', 'values', 'entries', 'merge', 'freeze', 'clone', 'sleep',
|
|
1765
|
-
'upper', 'lower', 'contains', 'starts_with', '
|
|
1766
|
-
'
|
|
1767
|
-
'
|
|
1768
|
-
'
|
|
1769
|
-
'
|
|
2006
|
+
'upper', 'lower', 'contains', 'starts_with', 'startsWith',
|
|
2007
|
+
'ends_with', 'endsWith', 'chars', 'words', 'lines', 'capitalize',
|
|
2008
|
+
'title_case', 'titleCase', 'snake_case', 'snakeCase',
|
|
2009
|
+
'camel_case', 'camelCase',
|
|
2010
|
+
'assert_eq', 'assertEq', 'assert_ne', 'assertNe', 'assert',
|
|
2011
|
+
'assert_throws', 'assertThrows',
|
|
2012
|
+
'create_spy', 'createSpy', 'create_mock', 'createMock',
|
|
2013
|
+
'parallel_map', 'parallelMap',
|
|
1770
2014
|
];
|
|
1771
2015
|
export const BUILTINS = _LEGACY_NAMES.map(n => BUILTIN_FUNCTIONS[n]).join('\n');
|
|
1772
2016
|
|
package/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by scripts/embed-runtime.js — do not edit
|
|
2
|
-
export const VERSION = "0.
|
|
2
|
+
export const VERSION = "0.10.1";
|