thyscript 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/thy-node.js +332 -0
- package/bin/thy.js +828 -0
- package/bin/thys.js +25 -0
- package/examples/calculator.thy +274 -0
- package/examples/calculator.thy.js +628 -0
- package/examples/hello.thy +34 -0
- package/examples/login.thy +382 -0
- package/examples/login.thy.js +710 -0
- package/package.json +54 -0
- package/scripts/_class.js +18 -0
- package/scripts/_class2.js +11 -0
- package/scripts/_class3.js +21 -0
- package/scripts/_dstruct.js +20 -0
- package/scripts/_dstruct2.js +24 -0
- package/scripts/_scaffold.js +16 -0
- package/scripts/_scaffold2.js +16 -0
- package/scripts/_scaffold3.js +26 -0
- package/scripts/_tpl_compile.js +13 -0
- package/scripts/_tpl_test.js +9 -0
- package/scripts/_typeof.js +26 -0
- package/scripts/_typeof2.js +25 -0
- package/scripts/patch_genmethod.js +44 -0
- package/scripts/smoke_test.js +167 -0
- package/scripts/smoke_test2.js +151 -0
- package/scripts/test_control_panel.js +58 -0
- package/src/compiler/CodeGenerator.js +1449 -0
- package/src/compiler/JITCompiler.js +325 -0
- package/src/database/DatabaseEngine.js +931 -0
- package/src/errors/ErrorDetector.js +305 -0
- package/src/errors/ErrorMessages.js +497 -0
- package/src/index.js +169 -0
- package/src/lexer/Lexer.js +342 -0
- package/src/lexer/TokenTypes.js +1234 -0
- package/src/memory/MemoryManager.js +283 -0
- package/src/parser/AST.js +606 -0
- package/src/parser/Parser.js +1942 -0
- package/src/runtime/ThyRuntime.js +265 -0
- package/src/runtime/stdlib/ArrayLib.js +207 -0
- package/src/runtime/stdlib/FileLib.js +168 -0
- package/src/runtime/stdlib/MathLib.js +411 -0
- package/src/runtime/stdlib/StringLib.js +387 -0
- package/src/server/ControlPanel.js +1188 -0
- package/src/server/HTMLCSSEngine.js +866 -0
- package/src/server/SecurityEngine.js +714 -0
- package/src/server/ServerEngine.js +738 -0
- package/tests/test1_math.thy +82 -0
- package/tests/test2_oop.thy +133 -0
- package/tests/test3_server.thy +143 -0
- package/tests/test4_functions.thy +159 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* ThyScript Runtime Engine
|
|
4
|
+
* Central runtime orchestrator for ThyScript execution.
|
|
5
|
+
* Manages memory, V8 garbage collection, server preprocessing,
|
|
6
|
+
* mathematical preprocessing, multi-core clustering, and standard library bindings.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const readline = require('readline');
|
|
12
|
+
const cluster = require('cluster');
|
|
13
|
+
const os = require('os');
|
|
14
|
+
const v8 = require('v8');
|
|
15
|
+
const vm = require('vm');
|
|
16
|
+
const { performance } = require('perf_hooks');
|
|
17
|
+
|
|
18
|
+
const MathLib = require('./stdlib/MathLib');
|
|
19
|
+
const StringLib = require('./stdlib/StringLib');
|
|
20
|
+
const ArrayLib = require('./stdlib/ArrayLib');
|
|
21
|
+
const FileLib = require('./stdlib/FileLib');
|
|
22
|
+
const MemoryManager = require('../memory/MemoryManager');
|
|
23
|
+
const ServerEngine = require('../server/ServerEngine');
|
|
24
|
+
const SecurityEngine = require('../server/SecurityEngine');
|
|
25
|
+
const HTMLCSSEngine = require('../server/HTMLCSSEngine');
|
|
26
|
+
const DatabaseEngine = require('../database/DatabaseEngine');
|
|
27
|
+
|
|
28
|
+
class ThyRuntime {
|
|
29
|
+
constructor(options = {}) {
|
|
30
|
+
this.options = Object.assign({
|
|
31
|
+
cores: Math.min(4, os.cpus().length || 4),
|
|
32
|
+
enableClustering: false,
|
|
33
|
+
memoryLimitMB: 512,
|
|
34
|
+
saveDir: process.cwd(),
|
|
35
|
+
verbose: false,
|
|
36
|
+
}, options);
|
|
37
|
+
|
|
38
|
+
this.memoryManager = new MemoryManager({
|
|
39
|
+
limitMB: this.options.memoryLimitMB,
|
|
40
|
+
onHighMemory: (stats) => {
|
|
41
|
+
if (this.options.verbose) {
|
|
42
|
+
console.warn(`[ThyScript Runtime] High memory usage: ${stats.heapUsedMB} MB / ${this.options.memoryLimitMB} MB`);
|
|
43
|
+
}
|
|
44
|
+
this.runGarbageCollector();
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
this.serverEngine = new ServerEngine();
|
|
49
|
+
this.securityEngine = new SecurityEngine();
|
|
50
|
+
this.htmlCssEngine = new HTMLCSSEngine();
|
|
51
|
+
this.databaseEngine = new DatabaseEngine();
|
|
52
|
+
this.mathLib = new MathLib(); // instance methods
|
|
53
|
+
this.stringLib = new StringLib(); // instance methods
|
|
54
|
+
this.arrayLib = ArrayLib; // static methods → expose the class
|
|
55
|
+
this.fileLib = FileLib; // static methods → expose the class
|
|
56
|
+
|
|
57
|
+
this._stateStore = new Map();
|
|
58
|
+
this._initialized = false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Initialize all runtime subsystems
|
|
63
|
+
*/
|
|
64
|
+
async init() {
|
|
65
|
+
if (this._initialized) return this;
|
|
66
|
+
|
|
67
|
+
// Start memory monitoring
|
|
68
|
+
this.memoryManager.startMonitoring(5000);
|
|
69
|
+
|
|
70
|
+
// Initialize 4-core cluster if requested
|
|
71
|
+
if (this.options.enableClustering && cluster.isPrimary) {
|
|
72
|
+
const numCores = this.options.cores;
|
|
73
|
+
for (let i = 0; i < numCores; i++) {
|
|
74
|
+
cluster.fork();
|
|
75
|
+
}
|
|
76
|
+
cluster.on('exit', (worker, code, signal) => {
|
|
77
|
+
if (this.options.verbose) {
|
|
78
|
+
console.log(`[ThyScript Cluster] Worker ${worker.process.pid} exited. Respawning...`);
|
|
79
|
+
}
|
|
80
|
+
cluster.fork();
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this._initialized = true;
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Run garbage collection (uses V8 global.gc if available, else clears internal caches)
|
|
90
|
+
*/
|
|
91
|
+
runGarbageCollector() {
|
|
92
|
+
if (global.gc) {
|
|
93
|
+
try {
|
|
94
|
+
global.gc();
|
|
95
|
+
} catch (_) {}
|
|
96
|
+
}
|
|
97
|
+
// Clean internal state store of stale entries
|
|
98
|
+
if (this._stateStore.size > 10000) {
|
|
99
|
+
this._stateStore.clear();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Prompt user for input via readline (async)
|
|
105
|
+
*/
|
|
106
|
+
async promptInput(promptText = '') {
|
|
107
|
+
const rl = readline.createInterface({
|
|
108
|
+
input: process.stdin,
|
|
109
|
+
output: process.stdout,
|
|
110
|
+
});
|
|
111
|
+
return new Promise((resolve) => {
|
|
112
|
+
rl.question(promptText ? `${promptText} ` : '> ', (answer) => {
|
|
113
|
+
rl.close();
|
|
114
|
+
resolve(answer.trim());
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Persist variable or state to disk (.thysave file)
|
|
121
|
+
*/
|
|
122
|
+
saveState(key, value) {
|
|
123
|
+
this._stateStore.set(key, value);
|
|
124
|
+
const savePath = path.join(this.options.saveDir, `${key}.thysave`);
|
|
125
|
+
try {
|
|
126
|
+
fs.writeFileSync(savePath, JSON.stringify(value, null, 2), 'utf8');
|
|
127
|
+
return true;
|
|
128
|
+
} catch (e) {
|
|
129
|
+
console.warn(`[ThyScript] Failed to persist state '${key}': ${e.message}`);
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Load persisted variable from disk
|
|
136
|
+
*/
|
|
137
|
+
loadState(key) {
|
|
138
|
+
if (this._stateStore.has(key)) return this._stateStore.get(key);
|
|
139
|
+
const savePath = path.join(this.options.saveDir, `${key}.thysave`);
|
|
140
|
+
if (fs.existsSync(savePath)) {
|
|
141
|
+
try {
|
|
142
|
+
const data = JSON.parse(fs.readFileSync(savePath, 'utf8'));
|
|
143
|
+
this._stateStore.set(key, data);
|
|
144
|
+
return data;
|
|
145
|
+
} catch (_) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Free an object or variable from memory (hint for garbage collection)
|
|
154
|
+
*/
|
|
155
|
+
freeVariable(obj) {
|
|
156
|
+
if (obj && typeof obj === 'object') {
|
|
157
|
+
if (Array.isArray(obj)) {
|
|
158
|
+
obj.length = 0;
|
|
159
|
+
} else {
|
|
160
|
+
for (const k of Object.keys(obj)) {
|
|
161
|
+
delete obj[k];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* High-speed math preprocessing and evaluation (target: sub-1ms)
|
|
170
|
+
*/
|
|
171
|
+
calculate(expr) {
|
|
172
|
+
if (typeof expr === 'number') return expr;
|
|
173
|
+
if (typeof expr !== 'string') return expr;
|
|
174
|
+
try {
|
|
175
|
+
return (new Function('Math', `return (${expr});`))(Math);
|
|
176
|
+
} catch (e) {
|
|
177
|
+
return this.mathLib.calculate(expr);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Print helper with formatting support
|
|
183
|
+
*/
|
|
184
|
+
print(...args) {
|
|
185
|
+
const formatted = args.map((arg) => {
|
|
186
|
+
if (arg === null) return 'null';
|
|
187
|
+
if (arg === undefined) return 'undefined';
|
|
188
|
+
if (typeof arg === 'object') {
|
|
189
|
+
try {
|
|
190
|
+
return JSON.stringify(arg, null, 2);
|
|
191
|
+
} catch (_) {
|
|
192
|
+
return String(arg);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return String(arg);
|
|
196
|
+
});
|
|
197
|
+
console.log(...formatted);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Build execution sandbox context with all ThyScript standard library functions
|
|
202
|
+
*/
|
|
203
|
+
buildContext(customContext = {}) {
|
|
204
|
+
const runtime = this;
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
// Core I/O
|
|
208
|
+
print: (...args) => runtime.print(...args),
|
|
209
|
+
input: (prompt) => runtime.promptInput(prompt),
|
|
210
|
+
save: (key, val) => runtime.saveState(key, val),
|
|
211
|
+
load: (key) => runtime.loadState(key),
|
|
212
|
+
free: (val) => runtime.freeVariable(val),
|
|
213
|
+
calculate: (expr) => runtime.calculate(expr),
|
|
214
|
+
|
|
215
|
+
// Internal compiler bindings
|
|
216
|
+
__thy_print: (...args) => runtime.print(...args),
|
|
217
|
+
__thy_input: (prompt) => runtime.promptInput(prompt),
|
|
218
|
+
__thy_save: (key, val) => runtime.saveState(key, val),
|
|
219
|
+
__thy_load: (key) => runtime.loadState(key),
|
|
220
|
+
__thy_free: (val) => runtime.freeVariable(val),
|
|
221
|
+
__thy_calculate: (expr) => runtime.calculate(expr),
|
|
222
|
+
__thy_sleep: (ms) => new Promise(r => setTimeout(r, ms)),
|
|
223
|
+
__thy_timer: () => performance.now(),
|
|
224
|
+
|
|
225
|
+
// Standard library packages
|
|
226
|
+
Math: Math,
|
|
227
|
+
math: runtime.mathLib,
|
|
228
|
+
string: runtime.stringLib,
|
|
229
|
+
array: runtime.arrayLib,
|
|
230
|
+
file: runtime.fileLib,
|
|
231
|
+
memory: runtime.memoryManager,
|
|
232
|
+
server: runtime.serverEngine,
|
|
233
|
+
security: runtime.securityEngine,
|
|
234
|
+
html: runtime.htmlCssEngine,
|
|
235
|
+
database: runtime.databaseEngine,
|
|
236
|
+
|
|
237
|
+
// Global constructors
|
|
238
|
+
Float64Array,
|
|
239
|
+
Float32Array,
|
|
240
|
+
Int32Array,
|
|
241
|
+
Uint8Array,
|
|
242
|
+
ArrayBuffer,
|
|
243
|
+
Map,
|
|
244
|
+
Set,
|
|
245
|
+
Promise,
|
|
246
|
+
Date,
|
|
247
|
+
RegExp,
|
|
248
|
+
JSON,
|
|
249
|
+
Buffer,
|
|
250
|
+
|
|
251
|
+
// Injected custom context
|
|
252
|
+
...customContext,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Clean up runtime resources
|
|
258
|
+
*/
|
|
259
|
+
destroy() {
|
|
260
|
+
this.memoryManager.stopMonitoring();
|
|
261
|
+
this.serverEngine.stop();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
module.exports = ThyRuntime;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* ThyScript Array Library
|
|
4
|
+
* High-performance array and collection manipulation functions
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
class ThyArrayLib {
|
|
8
|
+
// ─── Functional Iterators ──────────────────────────────────────────────────
|
|
9
|
+
static map(arr, fn) { return arr.map(fn); }
|
|
10
|
+
static filter(arr, fn) { return arr.filter(fn); }
|
|
11
|
+
static reduce(arr, fn, init) { return init !== undefined ? arr.reduce(fn, init) : arr.reduce(fn); }
|
|
12
|
+
static forEach(arr, fn) { arr.forEach(fn); }
|
|
13
|
+
static find(arr, fn) { return arr.find(fn); }
|
|
14
|
+
static findIndex(arr, fn) { return arr.findIndex(fn); }
|
|
15
|
+
static some(arr, fn) { return arr.some(fn); }
|
|
16
|
+
static every(arr, fn) { return arr.every(fn); }
|
|
17
|
+
static includes(arr, val) { return arr.includes(val); }
|
|
18
|
+
|
|
19
|
+
// ─── Sorting & Shuffling ───────────────────────────────────────────────────
|
|
20
|
+
static sort(arr, fn) { return [...arr].sort(fn); }
|
|
21
|
+
static sortBy(arr, key) {
|
|
22
|
+
const fn = typeof key === 'function' ? key : (item) => item[key];
|
|
23
|
+
return [...arr].sort((a, b) => {
|
|
24
|
+
const va = fn(a), vb = fn(b);
|
|
25
|
+
return va < vb ? -1 : va > vb ? 1 : 0;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
static sortDesc(arr) { return [...arr].sort((a, b) => b - a); }
|
|
29
|
+
static shuffle(arr) {
|
|
30
|
+
const result = [...arr];
|
|
31
|
+
for (let i = result.length - 1; i > 0; i--) {
|
|
32
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
33
|
+
[result[i], result[j]] = [result[j], result[i]];
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ─── Transformations ───────────────────────────────────────────────────────
|
|
39
|
+
static flatten(arr, depth = 1) { return arr.flat(depth); }
|
|
40
|
+
static flatMap(arr, fn) { return arr.flatMap(fn); }
|
|
41
|
+
static chunk(arr, size) {
|
|
42
|
+
const result = [];
|
|
43
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
44
|
+
result.push(arr.slice(i, i + size));
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
static zip(...arrays) {
|
|
49
|
+
const maxLen = Math.max(...arrays.map(a => a.length));
|
|
50
|
+
const result = [];
|
|
51
|
+
for (let i = 0; i < maxLen; i++) {
|
|
52
|
+
result.push(arrays.map(a => a[i]));
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
static unzip(arr) {
|
|
57
|
+
if (!arr.length) return [];
|
|
58
|
+
return arr[0].map((_, i) => arr.map(row => row[i]));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ─── Set Operations ────────────────────────────────────────────────────────
|
|
62
|
+
static unique(arr) { return [...new Set(arr)]; }
|
|
63
|
+
static uniqueBy(arr, key) {
|
|
64
|
+
const fn = typeof key === 'function' ? key : (item) => item[key];
|
|
65
|
+
const seen = new Set();
|
|
66
|
+
return arr.filter(item => {
|
|
67
|
+
const val = fn(item);
|
|
68
|
+
if (seen.has(val)) return false;
|
|
69
|
+
seen.add(val);
|
|
70
|
+
return true;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
static intersect(a, b) {
|
|
74
|
+
const setB = new Set(b);
|
|
75
|
+
return a.filter(x => setB.has(x));
|
|
76
|
+
}
|
|
77
|
+
static union(a, b) { return [...new Set([...a, ...b])]; }
|
|
78
|
+
static difference(a, b) {
|
|
79
|
+
const setB = new Set(b);
|
|
80
|
+
return a.filter(x => !setB.has(x));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ─── Grouping & Aggregation ────────────────────────────────────────────────
|
|
84
|
+
static groupBy(arr, key) {
|
|
85
|
+
const fn = typeof key === 'function' ? key : (item) => item[key];
|
|
86
|
+
return arr.reduce((acc, item) => {
|
|
87
|
+
const k = fn(item);
|
|
88
|
+
(acc[k] = acc[k] || []).push(item);
|
|
89
|
+
return acc;
|
|
90
|
+
}, {});
|
|
91
|
+
}
|
|
92
|
+
static countBy(arr, key) {
|
|
93
|
+
const fn = typeof key === 'function' ? key : (item) => item[key];
|
|
94
|
+
return arr.reduce((acc, item) => {
|
|
95
|
+
const k = fn(item);
|
|
96
|
+
acc[k] = (acc[k] || 0) + 1;
|
|
97
|
+
return acc;
|
|
98
|
+
}, {});
|
|
99
|
+
}
|
|
100
|
+
static partitionBy(arr, fn) {
|
|
101
|
+
const pass = [], fail = [];
|
|
102
|
+
for (const item of arr) {
|
|
103
|
+
(fn(item) ? pass : fail).push(item);
|
|
104
|
+
}
|
|
105
|
+
return [pass, fail];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ─── Accessors ─────────────────────────────────────────────────────────────
|
|
109
|
+
static first(arr, n = 1) { return n === 1 ? arr[0] : arr.slice(0, n); }
|
|
110
|
+
static last(arr, n = 1) { return n === 1 ? arr[arr.length - 1] : arr.slice(-n); }
|
|
111
|
+
static nth(arr, n) { return n < 0 ? arr[arr.length + n] : arr[n]; }
|
|
112
|
+
static take(arr, n) { return arr.slice(0, n); }
|
|
113
|
+
static drop(arr, n) { return arr.slice(n); }
|
|
114
|
+
static dropRight(arr, n) { return arr.slice(0, -n); }
|
|
115
|
+
|
|
116
|
+
// ─── Math & Statistics ─────────────────────────────────────────────────────
|
|
117
|
+
static sum(arr) { return arr.reduce((acc, x) => acc + x, 0); }
|
|
118
|
+
static avg(arr) { return arr.length ? arr.reduce((acc, x) => acc + x, 0) / arr.length : 0; }
|
|
119
|
+
static min(arr) { return Math.min(...arr); }
|
|
120
|
+
static max(arr) { return Math.max(...arr); }
|
|
121
|
+
static minBy(arr, key) {
|
|
122
|
+
const fn = typeof key === 'function' ? key : (item) => item[key];
|
|
123
|
+
return arr.reduce((min, item) => fn(item) < fn(min) ? item : min, arr[0]);
|
|
124
|
+
}
|
|
125
|
+
static maxBy(arr, key) {
|
|
126
|
+
const fn = typeof key === 'function' ? key : (item) => item[key];
|
|
127
|
+
return arr.reduce((max, item) => fn(item) > fn(max) ? item : max, arr[0]);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ─── Generation & Manipulation ─────────────────────────────────────────────
|
|
131
|
+
static range(start, end, step = 1) {
|
|
132
|
+
if (end === undefined) { end = start; start = 0; }
|
|
133
|
+
const result = [];
|
|
134
|
+
if (step > 0) {
|
|
135
|
+
for (let i = start; i < end; i += step) result.push(i);
|
|
136
|
+
} else {
|
|
137
|
+
for (let i = start; i > end; i += step) result.push(i);
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
static rotate(arr, n) {
|
|
142
|
+
const len = arr.length;
|
|
143
|
+
if (!len) return arr;
|
|
144
|
+
const offset = ((n % len) + len) % len;
|
|
145
|
+
return [...arr.slice(offset), ...arr.slice(0, offset)];
|
|
146
|
+
}
|
|
147
|
+
static reverse(arr) { return [...arr].reverse(); }
|
|
148
|
+
static compact(arr) { return arr.filter(Boolean); }
|
|
149
|
+
|
|
150
|
+
// ─── 2D Matrix Operations ──────────────────────────────────────────────────
|
|
151
|
+
static matrix2D(rows, cols, fillVal = 0) {
|
|
152
|
+
return Array.from({ length: rows }, () => new Array(cols).fill(fillVal));
|
|
153
|
+
}
|
|
154
|
+
static matrixTranspose(mat) {
|
|
155
|
+
if (!mat.length) return [];
|
|
156
|
+
return mat[0].map((_, c) => mat.map(row => row[c]));
|
|
157
|
+
}
|
|
158
|
+
static matrixMultiply(a, b) {
|
|
159
|
+
const rowsA = a.length, colsA = a[0].length, colsB = b[0].length;
|
|
160
|
+
const result = ThyArrayLib.matrix2D(rowsA, colsB, 0);
|
|
161
|
+
for (let i = 0; i < rowsA; i++) {
|
|
162
|
+
for (let j = 0; j < colsB; j++) {
|
|
163
|
+
let sum = 0;
|
|
164
|
+
for (let k = 0; k < colsA; k++) {
|
|
165
|
+
sum += a[i][k] * b[k][j];
|
|
166
|
+
}
|
|
167
|
+
result[i][j] = sum;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ─── Algorithms ────────────────────────────────────────────────────────────
|
|
174
|
+
static binarySearch(arr, target, compareFn) {
|
|
175
|
+
let low = 0, high = arr.length - 1;
|
|
176
|
+
const cmp = compareFn || ((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
177
|
+
while (low <= high) {
|
|
178
|
+
const mid = (low + high) >> 1;
|
|
179
|
+
const c = cmp(arr[mid], target);
|
|
180
|
+
if (c === 0) return mid;
|
|
181
|
+
if (c < 0) low = mid + 1;
|
|
182
|
+
else high = mid - 1;
|
|
183
|
+
}
|
|
184
|
+
return -1;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
static slidingWindow(arr, size, step = 1) {
|
|
188
|
+
const result = [];
|
|
189
|
+
for (let i = 0; i <= arr.length - size; i += step) {
|
|
190
|
+
result.push(arr.slice(i, i + size));
|
|
191
|
+
}
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
static movingAverage(arr, windowSize) {
|
|
196
|
+
const result = [];
|
|
197
|
+
let sum = 0;
|
|
198
|
+
for (let i = 0; i < arr.length; i++) {
|
|
199
|
+
sum += arr[i];
|
|
200
|
+
if (i >= windowSize) sum -= arr[i - windowSize];
|
|
201
|
+
if (i >= windowSize - 1) result.push(sum / windowSize);
|
|
202
|
+
}
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
module.exports = ThyArrayLib;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* ThyScript File Library
|
|
4
|
+
* Comprehensive file system operations with async/sync APIs, streams, and compression
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const zlib = require('zlib');
|
|
10
|
+
const { promisify } = require('util');
|
|
11
|
+
|
|
12
|
+
const gzip = promisify(zlib.gzip);
|
|
13
|
+
const gunzip = promisify(zlib.gunzip);
|
|
14
|
+
|
|
15
|
+
class ThyFileLib {
|
|
16
|
+
// ─── Reading ───────────────────────────────────────────────────────────────
|
|
17
|
+
static async read(filePath, encoding = 'utf8') {
|
|
18
|
+
return fs.promises.readFile(path.resolve(filePath), encoding);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static readSync(filePath, encoding = 'utf8') {
|
|
22
|
+
return fs.readFileSync(path.resolve(filePath), encoding);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static async readJSON(filePath) {
|
|
26
|
+
const content = await ThyFileLib.read(filePath);
|
|
27
|
+
return JSON.parse(content);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static readJSONSync(filePath) {
|
|
31
|
+
return JSON.parse(ThyFileLib.readSync(filePath));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static async readLines(filePath, encoding = 'utf8') {
|
|
35
|
+
const content = await ThyFileLib.read(filePath, encoding);
|
|
36
|
+
return content.split(/\r?\n/);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ─── Writing ───────────────────────────────────────────────────────────────
|
|
40
|
+
static async write(filePath, content, encoding = 'utf8') {
|
|
41
|
+
const resolved = path.resolve(filePath);
|
|
42
|
+
await fs.promises.mkdir(path.dirname(resolved), { recursive: true });
|
|
43
|
+
return fs.promises.writeFile(resolved, content, encoding);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static writeSync(filePath, content, encoding = 'utf8') {
|
|
47
|
+
const resolved = path.resolve(filePath);
|
|
48
|
+
fs.mkdirSync(path.dirname(resolved), { recursive: true });
|
|
49
|
+
return fs.writeFileSync(resolved, content, encoding);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static async writeJSON(filePath, obj, pretty = true) {
|
|
53
|
+
const content = pretty ? JSON.stringify(obj, null, 2) : JSON.stringify(obj);
|
|
54
|
+
return ThyFileLib.write(filePath, content);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static async append(filePath, content, encoding = 'utf8') {
|
|
58
|
+
const resolved = path.resolve(filePath);
|
|
59
|
+
await fs.promises.mkdir(path.dirname(resolved), { recursive: true });
|
|
60
|
+
return fs.promises.appendFile(resolved, content, encoding);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static async writeLines(filePath, lines, encoding = 'utf8') {
|
|
64
|
+
return ThyFileLib.write(filePath, lines.join('\n'), encoding);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ─── File & Directory Management ───────────────────────────────────────────
|
|
68
|
+
static exists(filePath) {
|
|
69
|
+
return fs.existsSync(path.resolve(filePath));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static async delete(filePath) {
|
|
73
|
+
const resolved = path.resolve(filePath);
|
|
74
|
+
const stat = await fs.promises.stat(resolved);
|
|
75
|
+
if (stat.isDirectory()) {
|
|
76
|
+
return fs.promises.rm(resolved, { recursive: true, force: true });
|
|
77
|
+
}
|
|
78
|
+
return fs.promises.unlink(resolved);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static async mkdir(dirPath) {
|
|
82
|
+
return fs.promises.mkdir(path.resolve(dirPath), { recursive: true });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static async listDir(dirPath, recursive = false) {
|
|
86
|
+
const resolved = path.resolve(dirPath);
|
|
87
|
+
if (!recursive) {
|
|
88
|
+
return fs.promises.readdir(resolved);
|
|
89
|
+
}
|
|
90
|
+
const entries = await fs.promises.readdir(resolved, { withFileTypes: true });
|
|
91
|
+
const files = [];
|
|
92
|
+
for (const entry of entries) {
|
|
93
|
+
const fullPath = path.join(resolved, entry.name);
|
|
94
|
+
if (entry.isDirectory()) {
|
|
95
|
+
const subFiles = await ThyFileLib.listDir(fullPath, true);
|
|
96
|
+
files.push(...subFiles);
|
|
97
|
+
} else {
|
|
98
|
+
files.push(fullPath);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return files;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static async copy(src, dst) {
|
|
105
|
+
const rSrc = path.resolve(src);
|
|
106
|
+
const rDst = path.resolve(dst);
|
|
107
|
+
await fs.promises.mkdir(path.dirname(rDst), { recursive: true });
|
|
108
|
+
return fs.promises.copyFile(rSrc, rDst);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static async move(src, dst) {
|
|
112
|
+
const rSrc = path.resolve(src);
|
|
113
|
+
const rDst = path.resolve(dst);
|
|
114
|
+
await fs.promises.mkdir(path.dirname(rDst), { recursive: true });
|
|
115
|
+
return fs.promises.rename(rSrc, rDst);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
static async stat(filePath) {
|
|
119
|
+
const st = await fs.promises.stat(path.resolve(filePath));
|
|
120
|
+
return {
|
|
121
|
+
size: st.size,
|
|
122
|
+
isFile: st.isFile(),
|
|
123
|
+
isDirectory: st.isDirectory(),
|
|
124
|
+
created: st.birthtime,
|
|
125
|
+
modified: st.mtime,
|
|
126
|
+
accessed: st.atime,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static watch(filePath, callback) {
|
|
131
|
+
return fs.watch(path.resolve(filePath), callback);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ─── Streams ───────────────────────────────────────────────────────────────
|
|
135
|
+
static readStream(filePath, options = {}) {
|
|
136
|
+
return fs.createReadStream(path.resolve(filePath), options);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
static writeStream(filePath, options = {}) {
|
|
140
|
+
return fs.createWriteStream(path.resolve(filePath), options);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ─── Compression ───────────────────────────────────────────────────────────
|
|
144
|
+
static async compress(srcPath, dstPath) {
|
|
145
|
+
const data = await fs.promises.readFile(path.resolve(srcPath));
|
|
146
|
+
const compressed = await gzip(data);
|
|
147
|
+
const target = dstPath || (srcPath + '.gz');
|
|
148
|
+
await fs.promises.writeFile(path.resolve(target), compressed);
|
|
149
|
+
return target;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
static async decompress(srcPath, dstPath) {
|
|
153
|
+
const data = await fs.promises.readFile(path.resolve(srcPath));
|
|
154
|
+
const decompressed = await gunzip(data);
|
|
155
|
+
const target = dstPath || srcPath.replace(/\.gz$/, '');
|
|
156
|
+
await fs.promises.writeFile(path.resolve(target), decompressed);
|
|
157
|
+
return target;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ─── Path Utilities ────────────────────────────────────────────────────────
|
|
161
|
+
static join(...parts) { return path.join(...parts); }
|
|
162
|
+
static resolve(...parts) { return path.resolve(...parts); }
|
|
163
|
+
static dirname(p) { return path.dirname(p); }
|
|
164
|
+
static basename(p, ext) { return path.basename(p, ext); }
|
|
165
|
+
static extname(p) { return path.extname(p); }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
module.exports = ThyFileLib;
|