jslike 1.4.0 → 1.4.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.
@@ -0,0 +1,17 @@
1
+ /**
2
+ * InMemoryModuleResolver compatibility for Wang tests
3
+ */
4
+
5
+ export class InMemoryModuleResolver {
6
+ constructor() {
7
+ this.modules = new Map();
8
+ }
9
+
10
+ addModule(name, code) {
11
+ this.modules.set(name, code);
12
+ }
13
+
14
+ async resolve(name) {
15
+ return this.modules.get(name);
16
+ }
17
+ }
@@ -0,0 +1,517 @@
1
+ // Built-in global objects and functions
2
+
3
+ export function createGlobalEnvironment(env) {
4
+ // Global objects (only those not already defined below)
5
+ env.define('Date', Date);
6
+
7
+ // console object
8
+ env.define('console', {
9
+ log: (...args) => {
10
+ console.log(...args);
11
+ return undefined;
12
+ },
13
+ error: (...args) => {
14
+ console.error(...args);
15
+ return undefined;
16
+ },
17
+ warn: (...args) => {
18
+ console.warn(...args);
19
+ return undefined;
20
+ },
21
+ info: (...args) => {
22
+ console.info(...args);
23
+ return undefined;
24
+ },
25
+ dir: (...args) => {
26
+ console.dir(...args);
27
+ return undefined;
28
+ }
29
+ });
30
+
31
+ // Math object
32
+ env.define('Math', {
33
+ PI: Math.PI,
34
+ E: Math.E,
35
+ abs: Math.abs,
36
+ acos: Math.acos,
37
+ asin: Math.asin,
38
+ atan: Math.atan,
39
+ atan2: Math.atan2,
40
+ ceil: Math.ceil,
41
+ cos: Math.cos,
42
+ exp: Math.exp,
43
+ floor: Math.floor,
44
+ log: Math.log,
45
+ max: Math.max,
46
+ min: Math.min,
47
+ pow: Math.pow,
48
+ random: Math.random,
49
+ round: Math.round,
50
+ sin: Math.sin,
51
+ sqrt: Math.sqrt,
52
+ tan: Math.tan,
53
+ trunc: Math.trunc
54
+ });
55
+
56
+ // Global values
57
+ env.define('undefined', undefined);
58
+
59
+ // Global functions
60
+ env.define('parseInt', parseInt);
61
+ env.define('parseFloat', parseFloat);
62
+ env.define('isNaN', isNaN);
63
+ env.define('isFinite', isFinite);
64
+
65
+ // Array constructor
66
+ env.define('Array', Array);
67
+
68
+ // Object constructor
69
+ env.define('Object', Object);
70
+
71
+ // String constructor
72
+ env.define('String', String);
73
+
74
+ // Number constructor
75
+ env.define('Number', Number);
76
+
77
+ // Boolean constructor
78
+ env.define('Boolean', Boolean);
79
+
80
+ // Function constructor
81
+ env.define('Function', Function);
82
+
83
+ // RegExp constructor
84
+ env.define('RegExp', RegExp);
85
+
86
+ // Symbol constructor
87
+ env.define('Symbol', Symbol);
88
+
89
+ // Map and Set constructors
90
+ env.define('Map', Map);
91
+ env.define('Set', Set);
92
+ env.define('WeakMap', WeakMap);
93
+ env.define('WeakSet', WeakSet);
94
+
95
+ // JSON object
96
+ env.define('JSON', {
97
+ parse: JSON.parse,
98
+ stringify: JSON.stringify
99
+ });
100
+
101
+ // setTimeout, setInterval (basic implementations)
102
+ env.define('setTimeout', setTimeout);
103
+ env.define('setInterval', setInterval);
104
+ env.define('clearTimeout', clearTimeout);
105
+ env.define('clearInterval', clearInterval);
106
+
107
+ // Promise
108
+ env.define('Promise', Promise);
109
+
110
+ // Error constructors
111
+ env.define('Error', Error);
112
+ env.define('TypeError', TypeError);
113
+ env.define('ReferenceError', ReferenceError);
114
+ env.define('SyntaxError', SyntaxError);
115
+ env.define('RangeError', RangeError);
116
+
117
+ // Global console functions (shortcuts for console.log/warn/error)
118
+ env.define('log', (...args) => {
119
+ console.log(...args);
120
+ return undefined;
121
+ });
122
+ env.define('warn', (...args) => {
123
+ console.warn(...args);
124
+ return undefined;
125
+ });
126
+ env.define('error', (...args) => {
127
+ console.error(...args);
128
+ return undefined;
129
+ });
130
+
131
+ // Wang Standard Library - Array Operations
132
+ env.define('sort_by', (array, keyOrFn) => {
133
+ const arr = [...array];
134
+ if (typeof keyOrFn === 'function') {
135
+ return arr.sort((a, b) => {
136
+ const aVal = keyOrFn(a);
137
+ const bVal = keyOrFn(b);
138
+ return aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
139
+ });
140
+ } else {
141
+ return arr.sort((a, b) => {
142
+ const aVal = a[keyOrFn];
143
+ const bVal = b[keyOrFn];
144
+ return aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
145
+ });
146
+ }
147
+ });
148
+
149
+ env.define('reverse', (array) => {
150
+ return [...array].reverse();
151
+ });
152
+
153
+ env.define('unique', (array) => {
154
+ return [...new Set(array)];
155
+ });
156
+
157
+ env.define('unique_by', (array, key) => {
158
+ const seen = new Set();
159
+ return array.filter(item => {
160
+ const val = item[key];
161
+ if (seen.has(val)) return false;
162
+ seen.add(val);
163
+ return true;
164
+ });
165
+ });
166
+
167
+ env.define('group_by', (array, key) => {
168
+ return array.reduce((groups, item) => {
169
+ const groupKey = item[key];
170
+ if (!groups[groupKey]) groups[groupKey] = [];
171
+ groups[groupKey].push(item);
172
+ return groups;
173
+ }, {});
174
+ });
175
+
176
+ env.define('chunk', (array, size) => {
177
+ const chunks = [];
178
+ for (let i = 0; i < array.length; i += size) {
179
+ chunks.push(array.slice(i, i + size));
180
+ }
181
+ return chunks;
182
+ });
183
+
184
+ env.define('flatten', (array, depth = 1) => {
185
+ return array.flat(depth);
186
+ });
187
+
188
+ env.define('first', (array, n) => {
189
+ if (n === undefined) return array[0];
190
+ return array.slice(0, n);
191
+ });
192
+
193
+ env.define('last', (array, n) => {
194
+ if (n === undefined) return array[array.length - 1];
195
+ return array.slice(-n);
196
+ });
197
+
198
+ env.define('take', (array, n) => {
199
+ return array.slice(0, n);
200
+ });
201
+
202
+ env.define('drop', (array, n) => {
203
+ return array.slice(n);
204
+ });
205
+
206
+ env.define('zip', (...arrays) => {
207
+ const length = Math.min(...arrays.map(a => a.length));
208
+ return Array.from({ length }, (_, i) => arrays.map(a => a[i]));
209
+ });
210
+
211
+ env.define('partition', (array, predicate) => {
212
+ const truthy = [];
213
+ const falsy = [];
214
+ array.forEach(item => {
215
+ if (predicate(item)) truthy.push(item);
216
+ else falsy.push(item);
217
+ });
218
+ return [truthy, falsy];
219
+ });
220
+
221
+ env.define('filter', (array, predicate) => {
222
+ return array.filter(predicate);
223
+ });
224
+
225
+ env.define('map', (array, fn) => {
226
+ return array.map(fn);
227
+ });
228
+
229
+ env.define('find', (array, predicate) => {
230
+ return array.find(predicate);
231
+ });
232
+
233
+ env.define('find_index', (array, predicate) => {
234
+ return array.findIndex(predicate);
235
+ });
236
+
237
+ env.define('every', (array, predicate) => {
238
+ return array.every(predicate);
239
+ });
240
+
241
+ env.define('some', (array, predicate) => {
242
+ return array.some(predicate);
243
+ });
244
+
245
+ env.define('count', (array, predicate) => {
246
+ if (!predicate) {
247
+ return array.length;
248
+ }
249
+ return array.filter(predicate).length;
250
+ });
251
+
252
+ // Wang Standard Library - Object Operations
253
+ env.define('keys', (obj) => {
254
+ return Object.keys(obj);
255
+ });
256
+
257
+ env.define('values', (obj) => {
258
+ return Object.values(obj);
259
+ });
260
+
261
+ env.define('entries', (obj) => {
262
+ return Object.entries(obj);
263
+ });
264
+
265
+ env.define('pick', (obj, keys) => {
266
+ const result = {};
267
+ keys.forEach(key => {
268
+ if (key in obj) result[key] = obj[key];
269
+ });
270
+ return result;
271
+ });
272
+
273
+ env.define('omit', (obj, keys) => {
274
+ const result = { ...obj };
275
+ keys.forEach(key => delete result[key]);
276
+ return result;
277
+ });
278
+
279
+ env.define('merge', (...objects) => {
280
+ return Object.assign({}, ...objects);
281
+ });
282
+
283
+ env.define('get', (obj, path, defaultValue) => {
284
+ const keys = path.split('.');
285
+ let current = obj;
286
+ for (const key of keys) {
287
+ if (current == null) return defaultValue;
288
+ current = current[key];
289
+ }
290
+ return current !== undefined ? current : defaultValue;
291
+ });
292
+
293
+ env.define('set', (obj, path, value) => {
294
+ const keys = path.split('.');
295
+ const result = JSON.parse(JSON.stringify(obj)); // Deep clone
296
+ let current = result;
297
+ for (let i = 0; i < keys.length - 1; i++) {
298
+ const key = keys[i];
299
+ if (!(key in current)) current[key] = {};
300
+ current = current[key];
301
+ }
302
+ current[keys[keys.length - 1]] = value;
303
+ return result;
304
+ });
305
+
306
+ env.define('clone', (obj) => {
307
+ return JSON.parse(JSON.stringify(obj));
308
+ });
309
+
310
+ // Wang Standard Library - String Operations
311
+ env.define('split', (str, separator) => {
312
+ return str.split(separator);
313
+ });
314
+
315
+ env.define('join', (array, separator) => {
316
+ return array.join(separator);
317
+ });
318
+
319
+ env.define('trim', (str) => {
320
+ return str.trim();
321
+ });
322
+
323
+ env.define('trim_start', (str) => {
324
+ return str.trimStart();
325
+ });
326
+
327
+ env.define('trim_end', (str) => {
328
+ return str.trimEnd();
329
+ });
330
+
331
+ env.define('upper', (str) => {
332
+ return str.toUpperCase();
333
+ });
334
+
335
+ env.define('toUpperCase', (str) => {
336
+ return str.toUpperCase();
337
+ });
338
+
339
+ env.define('lower', (str) => {
340
+ return str.toLowerCase();
341
+ });
342
+
343
+ env.define('toLowerCase', (str) => {
344
+ return str.toLowerCase();
345
+ });
346
+
347
+ env.define('capitalize', (str) => {
348
+ if (!str) return str;
349
+ return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
350
+ });
351
+
352
+ env.define('starts_with', (str, prefix) => {
353
+ return str.startsWith(prefix);
354
+ });
355
+
356
+ env.define('ends_with', (str, suffix) => {
357
+ return str.endsWith(suffix);
358
+ });
359
+
360
+ env.define('includes', (str, substring) => {
361
+ return str.includes(substring);
362
+ });
363
+
364
+ env.define('pad_start', (str, length, char = ' ') => {
365
+ return str.padStart(length, char);
366
+ });
367
+
368
+ env.define('pad_end', (str, length, char = ' ') => {
369
+ return str.padEnd(length, char);
370
+ });
371
+
372
+ env.define('truncate', (str, length) => {
373
+ if (str.length <= length) return str;
374
+ return str.slice(0, length - 3) + '...';
375
+ });
376
+
377
+ env.define('replace_all', (str, search, replace) => {
378
+ return str.replaceAll(search, replace);
379
+ });
380
+
381
+ // Wang Standard Library - Type Checking
382
+ env.define('is_string', (value) => {
383
+ return typeof value === 'string';
384
+ });
385
+
386
+ env.define('is_number', (value) => {
387
+ return typeof value === 'number';
388
+ });
389
+
390
+ env.define('is_boolean', (value) => {
391
+ return typeof value === 'boolean';
392
+ });
393
+
394
+ env.define('is_array', (value) => {
395
+ return Array.isArray(value);
396
+ });
397
+
398
+ env.define('is_object', (value) => {
399
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
400
+ });
401
+
402
+ env.define('is_function', (value) => {
403
+ return typeof value === 'function';
404
+ });
405
+
406
+ env.define('is_null', (value) => {
407
+ return value === null;
408
+ });
409
+
410
+ env.define('is_undefined', (value) => {
411
+ return value === undefined;
412
+ });
413
+
414
+ env.define('is_empty', (value) => {
415
+ if (value == null) return true;
416
+ if (typeof value === 'string' || Array.isArray(value)) return value.length === 0;
417
+ if (typeof value === 'object') return Object.keys(value).length === 0;
418
+ return false;
419
+ });
420
+
421
+ // Wang Standard Library - Math Operations
422
+ env.define('min', (array) => {
423
+ return Math.min(...array);
424
+ });
425
+
426
+ env.define('max', (array) => {
427
+ return Math.max(...array);
428
+ });
429
+
430
+ env.define('sum', (array) => {
431
+ return array.reduce((a, b) => a + b, 0);
432
+ });
433
+
434
+ env.define('avg', (array) => {
435
+ return array.reduce((a, b) => a + b, 0) / array.length;
436
+ });
437
+
438
+ env.define('median', (array) => {
439
+ const sorted = [...array].sort((a, b) => a - b);
440
+ const mid = Math.floor(sorted.length / 2);
441
+ return sorted.length % 2 === 0
442
+ ? (sorted[mid - 1] + sorted[mid]) / 2
443
+ : sorted[mid];
444
+ });
445
+
446
+ env.define('round', (num, decimals = 0) => {
447
+ const factor = Math.pow(10, decimals);
448
+ return Math.round(num * factor) / factor;
449
+ });
450
+
451
+ env.define('floor', (num) => {
452
+ return Math.floor(num);
453
+ });
454
+
455
+ env.define('ceil', (num) => {
456
+ return Math.ceil(num);
457
+ });
458
+
459
+ env.define('abs', (num) => {
460
+ return Math.abs(num);
461
+ });
462
+
463
+ env.define('clamp', (num, min, max) => {
464
+ return Math.min(Math.max(num, min), max);
465
+ });
466
+
467
+ env.define('range', (start, end, step) => {
468
+ // Support range(n) -> [0, 1, ..., n-1]
469
+ if (end === undefined) {
470
+ end = start;
471
+ start = 0;
472
+ step = 1;
473
+ }
474
+ if (step === undefined) {
475
+ step = start < end ? 1 : -1;
476
+ }
477
+
478
+ const result = [];
479
+ if (step > 0) {
480
+ for (let i = start; i < end; i += step) {
481
+ result.push(i);
482
+ }
483
+ } else {
484
+ for (let i = start; i > end; i += step) {
485
+ result.push(i);
486
+ }
487
+ }
488
+ return result;
489
+ });
490
+
491
+ // Wang Standard Library - Utility Functions
492
+ env.define('uuid', () => {
493
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
494
+ const r = Math.random() * 16 | 0;
495
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
496
+ return v.toString(16);
497
+ });
498
+ });
499
+
500
+ env.define('to_json', (value) => {
501
+ return JSON.stringify(value);
502
+ });
503
+
504
+ env.define('from_json', (str) => {
505
+ return JSON.parse(str);
506
+ });
507
+
508
+ env.define('sleep', async (ms) => {
509
+ return new Promise(resolve => setTimeout(resolve, ms));
510
+ });
511
+
512
+ env.define('wait', async (ms) => {
513
+ return new Promise(resolve => setTimeout(resolve, ms));
514
+ });
515
+
516
+ return env;
517
+ }
@@ -0,0 +1,85 @@
1
+ // Runtime Environment for variable scoping and storage
2
+
3
+ export class Environment {
4
+ constructor(parent = null) {
5
+ this.parent = parent;
6
+ this.vars = new Map();
7
+ this.consts = new Set(); // Track const variables
8
+ }
9
+
10
+ define(name, value, isConst = false) {
11
+ if (this.vars.has(name)) {
12
+ // Allow redeclaration for non-const (REPL-style behavior)
13
+ // But cannot redeclare a const
14
+ if (this.consts.has(name)) {
15
+ throw new Error(`Cannot redeclare const '${name}'`);
16
+ }
17
+ // Update existing variable
18
+ this.vars.set(name, value);
19
+ if (isConst) {
20
+ this.consts.add(name);
21
+ }
22
+ return value;
23
+ }
24
+ this.vars.set(name, value);
25
+ if (isConst) {
26
+ this.consts.add(name);
27
+ }
28
+ return value;
29
+ }
30
+
31
+ get(name) {
32
+ if (this.vars.has(name)) {
33
+ return this.vars.get(name);
34
+ }
35
+ if (this.parent) {
36
+ return this.parent.get(name);
37
+ }
38
+ throw new ReferenceError(`Variable "${name}" is not defined`);
39
+ }
40
+
41
+ set(name, value) {
42
+ if (this.vars.has(name)) {
43
+ // Check if trying to reassign a const variable
44
+ if (this.consts.has(name)) {
45
+ throw new TypeError(`Cannot reassign const variable '${name}'`);
46
+ }
47
+ this.vars.set(name, value);
48
+ return value;
49
+ }
50
+ if (this.parent) {
51
+ return this.parent.set(name, value);
52
+ }
53
+ throw new ReferenceError(`Variable "${name}" is not defined`);
54
+ }
55
+
56
+ has(name) {
57
+ return this.vars.has(name) || (this.parent ? this.parent.has(name) : false);
58
+ }
59
+
60
+ // For let/const block scoping
61
+ extend() {
62
+ return new Environment(this);
63
+ }
64
+ }
65
+
66
+ // Special control flow signals
67
+ export class ReturnValue {
68
+ constructor(value) {
69
+ this.value = value;
70
+ }
71
+ }
72
+
73
+ export class BreakSignal {
74
+ constructor() {}
75
+ }
76
+
77
+ export class ContinueSignal {
78
+ constructor() {}
79
+ }
80
+
81
+ export class ThrowSignal {
82
+ constructor(value) {
83
+ this.value = value;
84
+ }
85
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * WangValidator - Syntax validation for Wang/JSLike code
3
+ */
4
+
5
+ import { parse } from '../index.js';
6
+
7
+ export class WangValidator {
8
+ /**
9
+ * Validate code and return validation result
10
+ * @param {string} code - The code to validate
11
+ * @param {object} options - Validation options
12
+ * @param {boolean} options.includeAST - Whether to include AST in result
13
+ * @returns {object} Validation result
14
+ */
15
+ validate(code, options = {}) {
16
+ try {
17
+ // Parse the code
18
+ const ast = parse(code);
19
+
20
+ return {
21
+ valid: true,
22
+ ast: options.includeAST ? ast : undefined
23
+ };
24
+ } catch (error) {
25
+ // Extract line and column from error
26
+ const line = error.loc?.line || 1;
27
+ const column = error.loc?.column || 0;
28
+
29
+ // Generate suggestion for common errors
30
+ const suggestion = this.getSuggestion(error, code);
31
+
32
+ return {
33
+ valid: false,
34
+ error: {
35
+ message: error.message,
36
+ line,
37
+ column,
38
+ suggestion
39
+ }
40
+ };
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Generate suggestions for common syntax errors
46
+ * @param {Error} error - The parse error
47
+ * @param {string} code - The original code
48
+ * @returns {string|undefined} Suggestion text
49
+ */
50
+ getSuggestion(error, code) {
51
+ const message = error.message.toLowerCase();
52
+
53
+ // Common error patterns and suggestions
54
+ if (message.includes('unexpected token')) {
55
+ if (message.includes('}')) {
56
+ return 'Check for missing opening brace or extra closing brace';
57
+ }
58
+ if (message.includes('{')) {
59
+ return 'Check for missing closing brace or misplaced opening brace';
60
+ }
61
+ if (message.includes(')')) {
62
+ return 'Check for missing opening parenthesis or extra closing parenthesis';
63
+ }
64
+ if (message.includes('(')) {
65
+ return 'Check for missing closing parenthesis';
66
+ }
67
+ return 'Check syntax near the error location';
68
+ }
69
+
70
+ if (message.includes('unterminated string')) {
71
+ return 'Add closing quote to string literal';
72
+ }
73
+
74
+ if (message.includes('unterminated template')) {
75
+ return 'Add closing backtick to template literal';
76
+ }
77
+
78
+ if (message.includes('identifier expected')) {
79
+ return 'Provide a valid identifier name';
80
+ }
81
+
82
+ if (message.includes('unexpected end of input')) {
83
+ return 'Check for unclosed brackets, braces, or parentheses';
84
+ }
85
+
86
+ if (message.includes('invalid left-hand side')) {
87
+ return 'Check that assignment target is a valid variable or property';
88
+ }
89
+
90
+ if (message.includes('duplicate parameter')) {
91
+ return 'Remove duplicate parameter name in function definition';
92
+ }
93
+
94
+ if (message.includes('strict mode')) {
95
+ return 'This syntax is not allowed in strict mode';
96
+ }
97
+
98
+ return undefined;
99
+ }
100
+ }