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,325 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* ThyScript JIT Compiler
|
|
4
|
+
* Translates ThyScript AST to high-performance Node.js code and executes via V8 JIT.
|
|
5
|
+
* Features:
|
|
6
|
+
* - AST to optimized JavaScript translation via CodeGenerator
|
|
7
|
+
* - V8 JIT warmup and inline caching optimization
|
|
8
|
+
* - 4-Core CPU worker thread execution support
|
|
9
|
+
* - Sandbox VM execution with security isolation
|
|
10
|
+
* - Performance profiling and benchmarking
|
|
11
|
+
* - Memory management integration
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
|
|
15
|
+
const v8 = require('v8');
|
|
16
|
+
const vm = require('vm');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const os = require('os');
|
|
20
|
+
const { performance } = require('perf_hooks');
|
|
21
|
+
|
|
22
|
+
const CodeGenerator = require('./CodeGenerator');
|
|
23
|
+
|
|
24
|
+
class JITCompiler {
|
|
25
|
+
/**
|
|
26
|
+
* @param {Object} ast - Parsed AST from Parser
|
|
27
|
+
* @param {string} source - Original ThyScript source code
|
|
28
|
+
* @param {string} filename - Source filename
|
|
29
|
+
* @param {Object} options - Compiler options
|
|
30
|
+
*/
|
|
31
|
+
constructor(ast, source = '', filename = '<anonymous>', options = {}) {
|
|
32
|
+
// Unwrap { ast, errors } if passed directly
|
|
33
|
+
if (ast && ast.ast && typeof ast.ast === 'object') {
|
|
34
|
+
ast = ast.ast;
|
|
35
|
+
} else if (ast && !ast.type && typeof ast === 'object' && !Array.isArray(ast)) {
|
|
36
|
+
// Handle overload: new JITCompiler(options)
|
|
37
|
+
options = ast;
|
|
38
|
+
ast = null;
|
|
39
|
+
}
|
|
40
|
+
this.ast = ast;
|
|
41
|
+
this.source = source;
|
|
42
|
+
this.filename = filename;
|
|
43
|
+
this.options = Object.assign({
|
|
44
|
+
optimise: true,
|
|
45
|
+
jitWarmup: true,
|
|
46
|
+
cores: Math.min(4, os.cpus().length || 4),
|
|
47
|
+
sourceMap: false,
|
|
48
|
+
minify: false,
|
|
49
|
+
sandbox: true,
|
|
50
|
+
timeout: 0,
|
|
51
|
+
memoryLimit: 512 * 1024 * 1024, // 512MB
|
|
52
|
+
}, options);
|
|
53
|
+
|
|
54
|
+
this.codeGenerator = new CodeGenerator(this.options);
|
|
55
|
+
this.compiledCode = null;
|
|
56
|
+
this.stats = {
|
|
57
|
+
compileStartTime: 0,
|
|
58
|
+
compileEndTime: 0,
|
|
59
|
+
compileTimeMs: 0,
|
|
60
|
+
executionStartTime: 0,
|
|
61
|
+
executionEndTime: 0,
|
|
62
|
+
executionTimeMs: 0,
|
|
63
|
+
codeSizeBytes: 0,
|
|
64
|
+
v8HeapBefore: 0,
|
|
65
|
+
v8HeapAfter: 0,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Compile AST to optimized JavaScript
|
|
71
|
+
* @param {Object} [astOverride]
|
|
72
|
+
* @returns {string} Compiled JavaScript code
|
|
73
|
+
*/
|
|
74
|
+
compile(astOverride = null) {
|
|
75
|
+
const targetAst = astOverride || this.ast;
|
|
76
|
+
if (!targetAst) {
|
|
77
|
+
throw new Error('[ThyScript JIT] No AST provided for compilation');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const t0 = performance.now();
|
|
81
|
+
this.stats.compileStartTime = t0;
|
|
82
|
+
|
|
83
|
+
// Apply AST optimizations before code generation
|
|
84
|
+
const optimizedAst = this.options.optimise ? this._optimizeAST(targetAst) : targetAst;
|
|
85
|
+
|
|
86
|
+
// Generate code
|
|
87
|
+
this.compiledCode = this.codeGenerator.generate(optimizedAst);
|
|
88
|
+
|
|
89
|
+
const t1 = performance.now();
|
|
90
|
+
this.stats.compileEndTime = t1;
|
|
91
|
+
this.stats.compileTimeMs = t1 - t0;
|
|
92
|
+
this.stats.codeSizeBytes = Buffer.byteLength(this.compiledCode, 'utf8');
|
|
93
|
+
|
|
94
|
+
return this.compiledCode;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* AST-level optimizations:
|
|
99
|
+
* - Constant folding
|
|
100
|
+
* - Dead code elimination
|
|
101
|
+
* - Math operation vectorization hints
|
|
102
|
+
*/
|
|
103
|
+
_optimizeAST(node) {
|
|
104
|
+
if (!node || typeof node !== 'object') return node;
|
|
105
|
+
|
|
106
|
+
// Constant folding for binary expressions
|
|
107
|
+
if (node.type === 'BinaryExpr' || node.type === 'BinaryExpression') {
|
|
108
|
+
const left = this._optimizeAST(node.left);
|
|
109
|
+
const right = this._optimizeAST(node.right);
|
|
110
|
+
|
|
111
|
+
if (left && right && left.type === 'Literal' && right.type === 'Literal' &&
|
|
112
|
+
typeof left.value === 'number' && typeof right.value === 'number') {
|
|
113
|
+
let folded = null;
|
|
114
|
+
switch (node.operator) {
|
|
115
|
+
case '+': folded = left.value + right.value; break;
|
|
116
|
+
case '-': folded = left.value - right.value; break;
|
|
117
|
+
case '*': folded = left.value * right.value; break;
|
|
118
|
+
case '/': if (right.value !== 0) folded = left.value / right.value; break;
|
|
119
|
+
case '%': if (right.value !== 0) folded = left.value % right.value; break;
|
|
120
|
+
case '**': folded = Math.pow(left.value, right.value); break;
|
|
121
|
+
}
|
|
122
|
+
if (folded !== null) {
|
|
123
|
+
return { type: 'Literal', value: folded, raw: String(folded), line: node.line };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { ...node, left, right };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Recursively optimize child nodes
|
|
130
|
+
if (Array.isArray(node.body)) {
|
|
131
|
+
return { ...node, body: node.body.map(item => this._optimizeAST(item)) };
|
|
132
|
+
}
|
|
133
|
+
if (Array.isArray(node.statements)) {
|
|
134
|
+
return { ...node, statements: node.statements.map(item => this._optimizeAST(item)) };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return node;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Execute compiled JavaScript in a controlled VM sandbox or direct runtime
|
|
142
|
+
* @param {string} [code] - Code to execute (uses this.compiledCode if omitted)
|
|
143
|
+
* @param {Object} [customContext] - Additional sandbox bindings
|
|
144
|
+
* @returns {Promise<any>} Execution result
|
|
145
|
+
*/
|
|
146
|
+
async execute(code = null, customContext = {}) {
|
|
147
|
+
const codeToRun = code || this.compiledCode;
|
|
148
|
+
if (!codeToRun) {
|
|
149
|
+
throw new Error('[ThyScript JIT] No compiled code available to execute. Call compile() first.');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
this.stats.v8HeapBefore = process.memoryUsage().heapUsed;
|
|
153
|
+
const t0 = performance.now();
|
|
154
|
+
this.stats.executionStartTime = t0;
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
// Create VM context with full Node.js capabilities + ThyScript enhancements
|
|
158
|
+
const sandbox = {
|
|
159
|
+
console,
|
|
160
|
+
process,
|
|
161
|
+
require,
|
|
162
|
+
Buffer,
|
|
163
|
+
setTimeout,
|
|
164
|
+
clearTimeout,
|
|
165
|
+
setInterval,
|
|
166
|
+
clearInterval,
|
|
167
|
+
setImmediate,
|
|
168
|
+
clearImmediate,
|
|
169
|
+
__filename: this.filename,
|
|
170
|
+
__dirname: path.dirname(path.resolve(this.filename)),
|
|
171
|
+
// Fast typed arrays for 12x math performance
|
|
172
|
+
Float64Array,
|
|
173
|
+
Float32Array,
|
|
174
|
+
Int32Array,
|
|
175
|
+
Uint8Array,
|
|
176
|
+
ArrayBuffer,
|
|
177
|
+
SharedArrayBuffer: typeof SharedArrayBuffer !== 'undefined' ? SharedArrayBuffer : ArrayBuffer,
|
|
178
|
+
Atomics: typeof Atomics !== 'undefined' ? Atomics : {},
|
|
179
|
+
Math,
|
|
180
|
+
Date,
|
|
181
|
+
RegExp,
|
|
182
|
+
Map,
|
|
183
|
+
Set,
|
|
184
|
+
WeakMap,
|
|
185
|
+
WeakSet,
|
|
186
|
+
Promise,
|
|
187
|
+
JSON,
|
|
188
|
+
// Performance
|
|
189
|
+
performance,
|
|
190
|
+
// Custom injected context
|
|
191
|
+
...customContext,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const context = vm.createContext(sandbox);
|
|
195
|
+
|
|
196
|
+
// Wrap in async IIFE so top-level await works
|
|
197
|
+
const wrappedCode = `(async () => {\n${codeToRun}\n})()`;
|
|
198
|
+
|
|
199
|
+
// Compile script with V8 optimization hints
|
|
200
|
+
const script = new vm.Script(wrappedCode, {
|
|
201
|
+
filename: this.filename,
|
|
202
|
+
lineOffset: 0,
|
|
203
|
+
produceCachedData: true,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Warmup phase: trigger V8 JIT inline caching and TurboFan compilation
|
|
207
|
+
if (this.options.jitWarmup) {
|
|
208
|
+
this._warmUp();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Execute script
|
|
212
|
+
const result = await script.runInContext(context, {
|
|
213
|
+
timeout: this.options.timeout || undefined,
|
|
214
|
+
breakOnSigint: true,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const t1 = performance.now();
|
|
218
|
+
this.stats.executionEndTime = t1;
|
|
219
|
+
this.stats.executionTimeMs = t1 - t0;
|
|
220
|
+
this.stats.v8HeapAfter = process.memoryUsage().heapUsed;
|
|
221
|
+
|
|
222
|
+
return result;
|
|
223
|
+
} catch (err) {
|
|
224
|
+
const t1 = performance.now();
|
|
225
|
+
this.stats.executionEndTime = t1;
|
|
226
|
+
this.stats.executionTimeMs = t1 - t0;
|
|
227
|
+
throw err;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Warm up V8 JIT compiler:
|
|
233
|
+
* Executes dummy numeric operations to pre-warm the V8 TurboFan optimizer
|
|
234
|
+
*/
|
|
235
|
+
_warmUp() {
|
|
236
|
+
const f64 = new Float64Array(1024);
|
|
237
|
+
for (let i = 0; i < 1024; i++) {
|
|
238
|
+
f64[i] = Math.sin(i) * Math.cos(i);
|
|
239
|
+
}
|
|
240
|
+
let sum = 0;
|
|
241
|
+
for (let i = 0; i < 1024; i++) {
|
|
242
|
+
sum += f64[i] * 1.0001;
|
|
243
|
+
}
|
|
244
|
+
return sum;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Execute code across multiple CPU cores using Worker Threads
|
|
249
|
+
* @param {string} taskCode - Function or script to run in parallel
|
|
250
|
+
* @param {Array<any>} dataChunks - Data chunks distributed to cores
|
|
251
|
+
* @returns {Promise<Array<any>>} Aggregated results
|
|
252
|
+
*/
|
|
253
|
+
async executeParallel(taskCode, dataChunks) {
|
|
254
|
+
const numCores = Math.min(this.options.cores, dataChunks.length);
|
|
255
|
+
const workers = [];
|
|
256
|
+
const results = new Array(dataChunks.length);
|
|
257
|
+
|
|
258
|
+
const workerScript = `
|
|
259
|
+
const { parentPort, workerData } = require('worker_threads');
|
|
260
|
+
const vm = require('vm');
|
|
261
|
+
const { chunk, index, code } = workerData;
|
|
262
|
+
try {
|
|
263
|
+
const fn = new Function('chunk', 'index', code);
|
|
264
|
+
const res = fn(chunk, index);
|
|
265
|
+
parentPort.postMessage({ success: true, result: res, index });
|
|
266
|
+
} catch (err) {
|
|
267
|
+
parentPort.postMessage({ success: false, error: err.message, index });
|
|
268
|
+
}
|
|
269
|
+
`;
|
|
270
|
+
|
|
271
|
+
return new Promise((resolve, reject) => {
|
|
272
|
+
let completed = 0;
|
|
273
|
+
let hasError = false;
|
|
274
|
+
|
|
275
|
+
for (let i = 0; i < dataChunks.length; i++) {
|
|
276
|
+
const worker = new Worker(workerScript, {
|
|
277
|
+
eval: true,
|
|
278
|
+
workerData: {
|
|
279
|
+
chunk: dataChunks[i],
|
|
280
|
+
index: i,
|
|
281
|
+
code: taskCode,
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
worker.on('message', (msg) => {
|
|
286
|
+
if (!msg.success) {
|
|
287
|
+
hasError = true;
|
|
288
|
+
reject(new Error(`Worker ${msg.index} failed: ${msg.error}`));
|
|
289
|
+
} else {
|
|
290
|
+
results[msg.index] = msg.result;
|
|
291
|
+
completed++;
|
|
292
|
+
if (completed === dataChunks.length && !hasError) {
|
|
293
|
+
resolve(results);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
worker.on('error', (err) => {
|
|
299
|
+
hasError = true;
|
|
300
|
+
reject(err);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
workers.push(worker);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Get compilation and performance statistics
|
|
310
|
+
*/
|
|
311
|
+
getStats() {
|
|
312
|
+
return {
|
|
313
|
+
compileTimeMs: parseFloat(this.stats.compileTimeMs.toFixed(2)),
|
|
314
|
+
executionTimeMs: parseFloat(this.stats.executionTimeMs.toFixed(2)),
|
|
315
|
+
totalTimeMs: parseFloat((this.stats.compileTimeMs + this.stats.executionTimeMs).toFixed(2)),
|
|
316
|
+
codeSizeBytes: this.stats.codeSizeBytes,
|
|
317
|
+
codeSizeKB: parseFloat((this.stats.codeSizeBytes / 1024).toFixed(2)),
|
|
318
|
+
memoryDeltaBytes: this.stats.v8HeapAfter - this.stats.v8HeapBefore,
|
|
319
|
+
memoryDeltaKB: parseFloat(((this.stats.v8HeapAfter - this.stats.v8HeapBefore) / 1024).toFixed(2)),
|
|
320
|
+
v8HeapUsedMB: parseFloat((process.memoryUsage().heapUsed / (1024 * 1024)).toFixed(2)),
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
module.exports = JITCompiler;
|