thyscript 1.0.0 → 2.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/DOKUMEN.md +525 -0
- package/README.md +204 -0
- package/bin/thy-node.js +1 -1
- package/bin/thy.js +32 -21
- package/dist/calculator.thy.js +624 -0
- package/dist/thy-build.json +7 -0
- package/examples/calculator.thy +1 -0
- package/package.json +2 -2
- package/public/home.html +19 -0
- package/src/compiler/CodeGenerator.js +44 -6
- package/src/index.js +1 -1
- package/src/parser/Parser.js +41 -3
- package/src/server/ControlPanel.js +2 -2
- package/src/server/ServerEngine.js +13 -12
- package/tests/run-tests.js +40 -0
- package/tests/test_v2_features.thy +22 -0
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Auto-generated by ThyScript CodeGenerator – do not edit manually
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════
|
|
4
|
+
// ThyScript Standard Library – __thy_stdlib
|
|
5
|
+
// Auto-generated by ThyScript CodeGenerator
|
|
6
|
+
// ═══════════════════════════════════════════════════════════════
|
|
7
|
+
const __thy_stdlib = (() => {
|
|
8
|
+
'use strict';
|
|
9
|
+
const _fs = require('fs');
|
|
10
|
+
const _path = require('path');
|
|
11
|
+
const _os = require('os');
|
|
12
|
+
const _crypto = require('crypto');
|
|
13
|
+
|
|
14
|
+
// ── math ──────────────────────────────────────────────────────
|
|
15
|
+
const math = {
|
|
16
|
+
pi: Math.PI,
|
|
17
|
+
e: Math.E,
|
|
18
|
+
inf: Infinity,
|
|
19
|
+
nan: NaN,
|
|
20
|
+
epsilon: Number.EPSILON,
|
|
21
|
+
abs: (x) => Math.abs(x),
|
|
22
|
+
sqrt: (x) => Math.sqrt(x),
|
|
23
|
+
pow: (x, y) => Math.pow(x, y),
|
|
24
|
+
floor: (x) => Math.floor(x),
|
|
25
|
+
ceil: (x) => Math.ceil(x),
|
|
26
|
+
round: (x) => Math.round(x),
|
|
27
|
+
sin: (x) => Math.sin(x),
|
|
28
|
+
cos: (x) => Math.cos(x),
|
|
29
|
+
tan: (x) => Math.tan(x),
|
|
30
|
+
asin: (x) => Math.asin(x),
|
|
31
|
+
acos: (x) => Math.acos(x),
|
|
32
|
+
atan: (x) => Math.atan(x),
|
|
33
|
+
atan2: (y, x) => Math.atan2(y, x),
|
|
34
|
+
log: (x) => Math.log(x),
|
|
35
|
+
log2: (x) => Math.log2(x),
|
|
36
|
+
log10: (x) => Math.log10(x),
|
|
37
|
+
exp: (x) => Math.exp(x),
|
|
38
|
+
random: () => Math.random(),
|
|
39
|
+
min: (...args) => Math.min(...args),
|
|
40
|
+
max: (...args) => Math.max(...args),
|
|
41
|
+
clamp: (v, lo, hi) => Math.max(lo, Math.min(hi, v)),
|
|
42
|
+
lerp: (a, b, t) => a + (b - a) * t,
|
|
43
|
+
hypot: (...args) => Math.hypot(...args),
|
|
44
|
+
sign: (x) => Math.sign(x),
|
|
45
|
+
trunc: (x) => Math.trunc(x),
|
|
46
|
+
cbrt: (x) => Math.cbrt(x),
|
|
47
|
+
// Fast integer math via typed arrays
|
|
48
|
+
imul: (a, b) => Math.imul(a, b),
|
|
49
|
+
fround: (x) => Math.fround(x),
|
|
50
|
+
clz32: (x) => Math.clz32(x),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Fast-math helpers using Float64Array for type stability
|
|
54
|
+
const _buf64 = new Float64Array(1);
|
|
55
|
+
const _buf32 = new Float32Array(1);
|
|
56
|
+
const fastFloor = (x) => { _buf64[0] = x; return x | 0; };
|
|
57
|
+
const fastAbs = (x) => x < 0 ? -x : x;
|
|
58
|
+
const fastMod = (a, b) => a - b * ((a / b) | 0);
|
|
59
|
+
|
|
60
|
+
// ── vectors ───────────────────────────────────────────────────
|
|
61
|
+
const vec = {
|
|
62
|
+
add: (a, b) => a.map((v, i) => v + b[i]),
|
|
63
|
+
sub: (a, b) => a.map((v, i) => v - b[i]),
|
|
64
|
+
scale: (a, s) => a.map(v => v * s),
|
|
65
|
+
dot: (a, b) => a.reduce((acc, v, i) => acc + v * b[i], 0),
|
|
66
|
+
cross3: (a, b) => [
|
|
67
|
+
a[1]*b[2] - a[2]*b[1],
|
|
68
|
+
a[2]*b[0] - a[0]*b[2],
|
|
69
|
+
a[0]*b[1] - a[1]*b[0],
|
|
70
|
+
],
|
|
71
|
+
length: (a) => Math.hypot(...a),
|
|
72
|
+
normalize: (a) => { const len = Math.hypot(...a) || 1; return a.map(v => v / len); },
|
|
73
|
+
distance: (a, b) => Math.hypot(...a.map((v, i) => v - b[i])),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// ── string helpers ────────────────────────────────────────────
|
|
77
|
+
const str = {
|
|
78
|
+
upper: (s) => String(s).toUpperCase(),
|
|
79
|
+
lower: (s) => String(s).toLowerCase(),
|
|
80
|
+
trim: (s) => String(s).trim(),
|
|
81
|
+
split: (s, sep) => String(s).split(sep),
|
|
82
|
+
replace: (s, pat, rep) => String(s).replace(pat, rep),
|
|
83
|
+
includes:(s, sub) => String(s).includes(sub),
|
|
84
|
+
startsWith:(s, pre) => String(s).startsWith(pre),
|
|
85
|
+
endsWith: (s, suf) => String(s).endsWith(suf),
|
|
86
|
+
repeat: (s, n) => String(s).repeat(n),
|
|
87
|
+
pad: (s, n, ch = ' ') => String(s).padStart(n, ch),
|
|
88
|
+
padEnd: (s, n, ch = ' ') => String(s).padEnd(n, ch),
|
|
89
|
+
indexOf: (s, sub) => String(s).indexOf(sub),
|
|
90
|
+
slice: (s, start, end) => String(s).slice(start, end),
|
|
91
|
+
charCode:(s, i = 0) => String(s).charCodeAt(i),
|
|
92
|
+
fromCode:(n) => String.fromCharCode(n),
|
|
93
|
+
reverse: (s) => String(s).split('').reverse().join(''),
|
|
94
|
+
count: (s, sub) => { let c = 0, i = 0; while ((i = s.indexOf(sub, i)) !== -1) { c++; i++; } return c; },
|
|
95
|
+
format: (tpl, ...args) => tpl.replace(/{(\d+)}/g, (_, i) => args[i] ?? ''),
|
|
96
|
+
escape: (s) => String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'),
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// ── array helpers ─────────────────────────────────────────────
|
|
100
|
+
const arr = {
|
|
101
|
+
map: (a, fn) => Array.from(a).map(fn),
|
|
102
|
+
filter: (a, fn) => Array.from(a).filter(fn),
|
|
103
|
+
reduce: (a, fn, init) => Array.from(a).reduce(fn, init),
|
|
104
|
+
find: (a, fn) => Array.from(a).find(fn),
|
|
105
|
+
findIdx: (a, fn) => Array.from(a).findIndex(fn),
|
|
106
|
+
sort: (a, fn) => [...a].sort(fn),
|
|
107
|
+
flat: (a, d = 1) => Array.from(a).flat(d),
|
|
108
|
+
flatMap: (a, fn) => Array.from(a).flatMap(fn),
|
|
109
|
+
unique: (a) => [...new Set(a)],
|
|
110
|
+
zip: (...arrs) => arrs[0].map((_, i) => arrs.map(a => a[i])),
|
|
111
|
+
chunk: (a, n) => { const res = []; for (let i = 0; i < a.length; i += n) res.push(a.slice(i, i+n)); return res; },
|
|
112
|
+
shuffle: (a) => { const r = [...a]; for (let i = r.length-1; i > 0; i--) { const j = Math.floor(Math.random()*(i+1)); [r[i],r[j]]=[r[j],r[i]]; } return r; },
|
|
113
|
+
range: (start, end, step = 1) => { const r = []; for (let i = start; i < end; i += step) r.push(i); return r; },
|
|
114
|
+
sum: (a) => a.reduce((acc, v) => acc + v, 0),
|
|
115
|
+
avg: (a) => a.reduce((acc, v) => acc + v, 0) / a.length,
|
|
116
|
+
min: (a) => Math.min(...a),
|
|
117
|
+
max: (a) => Math.max(...a),
|
|
118
|
+
includes:(a, v) => Array.from(a).includes(v),
|
|
119
|
+
count: (a, v) => Array.from(a).filter(x => x === v).length,
|
|
120
|
+
first: (a) => a[0],
|
|
121
|
+
last: (a) => a[a.length - 1],
|
|
122
|
+
take: (a, n) => a.slice(0, n),
|
|
123
|
+
drop: (a, n) => a.slice(n),
|
|
124
|
+
reverse: (a) => [...a].reverse(),
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// ── object helpers ────────────────────────────────────────────
|
|
128
|
+
const obj = {
|
|
129
|
+
keys: (o) => Object.keys(o),
|
|
130
|
+
values: (o) => Object.values(o),
|
|
131
|
+
entries: (o) => Object.entries(o),
|
|
132
|
+
merge: (...os) => Object.assign({}, ...os),
|
|
133
|
+
clone: (o) => JSON.parse(JSON.stringify(o)),
|
|
134
|
+
pick: (o, keys) => keys.reduce((acc, k) => { if (k in o) acc[k] = o[k]; return acc; }, {}),
|
|
135
|
+
omit: (o, keys) => { const r = {...o}; keys.forEach(k => delete r[k]); return r; },
|
|
136
|
+
has: (o, k) => Object.prototype.hasOwnProperty.call(o, k),
|
|
137
|
+
freeze: (o) => Object.freeze(o),
|
|
138
|
+
seal: (o) => Object.seal(o),
|
|
139
|
+
fromEntries: (e) => Object.fromEntries(e),
|
|
140
|
+
assign: (target, ...sources) => Object.assign(target, ...sources),
|
|
141
|
+
flatten: (o, prefix = '') => {
|
|
142
|
+
return Object.keys(o).reduce((acc, k) => {
|
|
143
|
+
const key = prefix ? prefix + '.' + k : k;
|
|
144
|
+
if (typeof o[k] === 'object' && o[k] !== null && !Array.isArray(o[k]))
|
|
145
|
+
Object.assign(acc, obj.flatten(o[k], key));
|
|
146
|
+
else acc[key] = o[k];
|
|
147
|
+
return acc;
|
|
148
|
+
}, {});
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// ── I/O helpers ───────────────────────────────────────────────
|
|
153
|
+
const io = {
|
|
154
|
+
print: (...args) => { process.stdout.write(args.map(a => __thy_stringify(a)).join(' ') + '\n'); },
|
|
155
|
+
printErr:(...args) => { process.stderr.write(args.map(a => __thy_stringify(a)).join(' ') + '\n'); },
|
|
156
|
+
write: (s) => { process.stdout.write(String(s)); },
|
|
157
|
+
read: (fp) => _fs.readFileSync(fp, 'utf8'),
|
|
158
|
+
readLines:(fp) => _fs.readFileSync(fp, 'utf8').split('\n'),
|
|
159
|
+
write_: (fp, data) => _fs.writeFileSync(fp, data, 'utf8'),
|
|
160
|
+
append: (fp, data) => _fs.appendFileSync(fp, data, 'utf8'),
|
|
161
|
+
exists: (fp) => _fs.existsSync(fp),
|
|
162
|
+
mkdir: (fp) => _fs.mkdirSync(fp, { recursive: true }),
|
|
163
|
+
readdir: (fp) => _fs.readdirSync(fp),
|
|
164
|
+
rm: (fp) => _fs.rmSync(fp, { recursive: true, force: true }),
|
|
165
|
+
stat: (fp) => _fs.statSync(fp),
|
|
166
|
+
isFile: (fp) => _fs.existsSync(fp) && _fs.statSync(fp).isFile(),
|
|
167
|
+
isDir: (fp) => _fs.existsSync(fp) && _fs.statSync(fp).isDirectory(),
|
|
168
|
+
cwd: () => process.cwd(),
|
|
169
|
+
home: () => _os.homedir(),
|
|
170
|
+
tempDir: () => _os.tmpdir(),
|
|
171
|
+
sep: _path.sep,
|
|
172
|
+
join: (...parts) => _path.join(...parts),
|
|
173
|
+
resolve: (...parts) => _path.resolve(...parts),
|
|
174
|
+
dirname: (p) => _path.dirname(p),
|
|
175
|
+
basename:(p, ext) => _path.basename(p, ext),
|
|
176
|
+
ext: (p) => _path.extname(p),
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// ── crypto helpers ────────────────────────────────────────────
|
|
180
|
+
const security = {
|
|
181
|
+
hash: (data, alg = 'sha256') => _crypto.createHash(alg).update(String(data)).digest('hex'),
|
|
182
|
+
hmac: (key, data, alg = 'sha256') => _crypto.createHmac(alg, key).update(String(data)).digest('hex'),
|
|
183
|
+
random: (n = 16) => _crypto.randomBytes(n).toString('hex'),
|
|
184
|
+
uuid: () => _crypto.randomUUID ? _crypto.randomUUID() : _crypto.randomBytes(16).toString('hex').replace(/(........)(....)(....)(....)(............)/, '$1-$2-$3-$4-$5'),
|
|
185
|
+
encrypt: (text, key) => {
|
|
186
|
+
const iv = _crypto.randomBytes(16);
|
|
187
|
+
const k = _crypto.createHash('sha256').update(key).digest();
|
|
188
|
+
const c = _crypto.createCipheriv('aes-256-cbc', k, iv);
|
|
189
|
+
return iv.toString('hex') + ':' + Buffer.concat([c.update(text), c.final()]).toString('hex');
|
|
190
|
+
},
|
|
191
|
+
decrypt: (enc, key) => {
|
|
192
|
+
const [ivHex, data] = enc.split(':');
|
|
193
|
+
const iv = Buffer.from(ivHex, 'hex');
|
|
194
|
+
const k = _crypto.createHash('sha256').update(key).digest();
|
|
195
|
+
const d = _crypto.createDecipheriv('aes-256-cbc', k, iv);
|
|
196
|
+
return Buffer.concat([d.update(Buffer.from(data, 'hex')), d.final()]).toString();
|
|
197
|
+
},
|
|
198
|
+
base64encode: (s) => Buffer.from(String(s)).toString('base64'),
|
|
199
|
+
base64decode: (s) => Buffer.from(String(s), 'base64').toString(),
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// ── type helpers ──────────────────────────────────────────────
|
|
203
|
+
const types = {
|
|
204
|
+
isNum: (v) => typeof v === 'number' && !isNaN(v),
|
|
205
|
+
isStr: (v) => typeof v === 'string',
|
|
206
|
+
isBool: (v) => typeof v === 'boolean',
|
|
207
|
+
isNull: (v) => v === null,
|
|
208
|
+
isUndef: (v) => v === undefined,
|
|
209
|
+
isArr: (v) => Array.isArray(v),
|
|
210
|
+
isObj: (v) => v !== null && typeof v === 'object' && !Array.isArray(v),
|
|
211
|
+
isFn: (v) => typeof v === 'function',
|
|
212
|
+
type: (v) => v === null ? 'null' : Array.isArray(v) ? 'array' : typeof v,
|
|
213
|
+
toNum: (v) => Number(v),
|
|
214
|
+
toStr: (v) => String(v),
|
|
215
|
+
toBool: (v) => Boolean(v),
|
|
216
|
+
toInt: (v) => parseInt(v, 10),
|
|
217
|
+
toFloat: (v) => parseFloat(v),
|
|
218
|
+
toArr: (v) => Array.isArray(v) ? v : [v],
|
|
219
|
+
toJson: (v) => JSON.stringify(v, null, 2),
|
|
220
|
+
fromJson:(v) => JSON.parse(v),
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// ── time helpers ──────────────────────────────────────────────
|
|
224
|
+
const time = {
|
|
225
|
+
now: () => Date.now(),
|
|
226
|
+
date: () => new Date(),
|
|
227
|
+
sleep: (ms) => new Promise(r => setTimeout(r, ms)),
|
|
228
|
+
perf: () => typeof performance !== 'undefined' ? performance.now() : Number(process.hrtime.bigint()) / 1e6,
|
|
229
|
+
format: (d = new Date()) => d.toISOString(),
|
|
230
|
+
unix: () => Math.floor(Date.now() / 1000),
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// ── async/process helpers ─────────────────────────────────────
|
|
234
|
+
const proc = {
|
|
235
|
+
env: (k, def = '') => process.env[k] || def,
|
|
236
|
+
args: () => process.argv.slice(2),
|
|
237
|
+
exit: (code = 0) => process.exit(code),
|
|
238
|
+
pid: () => process.pid,
|
|
239
|
+
memUsage:() => process.memoryUsage(),
|
|
240
|
+
cpuUsage:() => process.cpuUsage(),
|
|
241
|
+
platform:() => process.platform,
|
|
242
|
+
arch: () => process.arch,
|
|
243
|
+
version: () => process.version,
|
|
244
|
+
hrtime: () => process.hrtime.bigint(),
|
|
245
|
+
nextTick:(fn) => process.nextTick(fn),
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// ── http helpers ──────────────────────────────────────────────
|
|
249
|
+
const http = {
|
|
250
|
+
fetch: async (url, opts = {}) => {
|
|
251
|
+
const https = url.startsWith('https') ? require('https') : require('http');
|
|
252
|
+
return new Promise((resolve, reject) => {
|
|
253
|
+
const req = https.request(url, { method: opts.method || 'GET', headers: opts.headers || {} }, (res) => {
|
|
254
|
+
let data = '';
|
|
255
|
+
res.on('data', chunk => data += chunk);
|
|
256
|
+
res.on('end', () => resolve({ status: res.statusCode, headers: res.headers, body: data,
|
|
257
|
+
json: () => JSON.parse(data), text: () => data }));
|
|
258
|
+
});
|
|
259
|
+
req.on('error', reject);
|
|
260
|
+
if (opts.body) req.write(typeof opts.body === 'string' ? opts.body : JSON.stringify(opts.body));
|
|
261
|
+
req.end();
|
|
262
|
+
});
|
|
263
|
+
},
|
|
264
|
+
get: async (url, headers = {}) => http.fetch(url, { method: 'GET', headers }),
|
|
265
|
+
post: async (url, body, headers = {}) => http.fetch(url, { method: 'POST', body, headers: { 'Content-Type': 'application/json', ...headers } }),
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// ── persist helpers ───────────────────────────────────────────
|
|
269
|
+
const persist = {
|
|
270
|
+
save: (name, data) => {
|
|
271
|
+
const fp = _path.join(process.cwd(), name + '.thysave');
|
|
272
|
+
_fs.writeFileSync(fp, JSON.stringify(data, null, 2), 'utf8');
|
|
273
|
+
},
|
|
274
|
+
load: (name) => {
|
|
275
|
+
const fp = _path.join(process.cwd(), name + '.thysave');
|
|
276
|
+
if (!_fs.existsSync(fp)) return null;
|
|
277
|
+
return JSON.parse(_fs.readFileSync(fp, 'utf8'));
|
|
278
|
+
},
|
|
279
|
+
del: (name) => {
|
|
280
|
+
const fp = _path.join(process.cwd(), name + '.thysave');
|
|
281
|
+
if (_fs.existsSync(fp)) _fs.unlinkSync(fp);
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// ── optimised calculate ───────────────────────────────────────
|
|
286
|
+
const __thy_calculate = (expr) => {
|
|
287
|
+
// expr is already a JS expression string – evaluate safely
|
|
288
|
+
// This is called at runtime for dynamic calculate() statements
|
|
289
|
+
try {
|
|
290
|
+
// Use Function constructor for safe, optimisable evaluation
|
|
291
|
+
return (new Function('Math', `return (${expr});`))(Math);
|
|
292
|
+
} catch(e) {
|
|
293
|
+
throw new TypeError('[ThyScript] calculate error: ' + e.message);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
return Object.freeze({
|
|
298
|
+
math, vec, str, arr, obj, io, security, types, time, proc, http, persist,
|
|
299
|
+
fastFloor, fastAbs, fastMod,
|
|
300
|
+
__thy_calculate,
|
|
301
|
+
// convenience top-level aliases
|
|
302
|
+
print: io.print,
|
|
303
|
+
len: (v) => (Array.isArray(v) || typeof v === 'string') ? v.length : Object.keys(v).length,
|
|
304
|
+
range: arr.range,
|
|
305
|
+
type: types.type,
|
|
306
|
+
hash: security.hash,
|
|
307
|
+
sleep: time.sleep,
|
|
308
|
+
now: time.now,
|
|
309
|
+
env: proc.env,
|
|
310
|
+
fetch: http.fetch,
|
|
311
|
+
save: persist.save,
|
|
312
|
+
load: persist.load,
|
|
313
|
+
});
|
|
314
|
+
})();
|
|
315
|
+
|
|
316
|
+
// Convenience globals injected from stdlib
|
|
317
|
+
const __thy_print = __thy_stdlib.print;
|
|
318
|
+
const __thy_len = __thy_stdlib.len;
|
|
319
|
+
const __thy_range = __thy_stdlib.range;
|
|
320
|
+
const __thy_sleep = __thy_stdlib.time.sleep;
|
|
321
|
+
|
|
322
|
+
function __thy_stringify(v) {
|
|
323
|
+
if (v === null) return 'null';
|
|
324
|
+
if (v === undefined) return 'undefined';
|
|
325
|
+
if (typeof v === 'object') { try { return JSON.stringify(v); } catch(_) { return String(v); } }
|
|
326
|
+
return String(v);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Shared typed buffers for hot numeric paths
|
|
330
|
+
const __thy_f64 = new Float64Array(256);
|
|
331
|
+
const __thy_i32 = new Int32Array(256);
|
|
332
|
+
const __thy_sab = typeof SharedArrayBuffer !== 'undefined' ? new SharedArrayBuffer(4096) : null;
|
|
333
|
+
|
|
334
|
+
const __thy_express = (() => { try { return require('express'); } catch(_) { return null; } })();
|
|
335
|
+
const __thy_app = __thy_express ? __thy_express() : null;
|
|
336
|
+
|
|
337
|
+
// ── program body ──────────────────────────────────────────────
|
|
338
|
+
(async () => {
|
|
339
|
+
let port = 3000;
|
|
340
|
+
let title = 'ThyScript Advanced Calculator';
|
|
341
|
+
// [public] class
|
|
342
|
+
class Calculator {
|
|
343
|
+
constructor() {
|
|
344
|
+
this.value = 0;
|
|
345
|
+
this.operator = '';
|
|
346
|
+
this.previousValue = 0;
|
|
347
|
+
this.display = '0';
|
|
348
|
+
this.isNewNumber = true;
|
|
349
|
+
}
|
|
350
|
+
clear() {
|
|
351
|
+
const compute = (...args) => this.compute(...args);
|
|
352
|
+
let { value, operator, previousValue, display, isNewNumber } = this;
|
|
353
|
+
value = 0;
|
|
354
|
+
operator = '';
|
|
355
|
+
previousValue = 0;
|
|
356
|
+
display = '0';
|
|
357
|
+
isNewNumber = true;
|
|
358
|
+
Object.assign(this, { value, operator, previousValue, display, isNewNumber });
|
|
359
|
+
}
|
|
360
|
+
compute(op, a, b) {
|
|
361
|
+
const clear = (...args) => this.clear(...args);
|
|
362
|
+
let { value, operator, previousValue, display, isNewNumber } = this;
|
|
363
|
+
if ((op === '+')) {
|
|
364
|
+
return ((a + b));
|
|
365
|
+
}
|
|
366
|
+
if ((op === '-')) {
|
|
367
|
+
return ((a - b));
|
|
368
|
+
}
|
|
369
|
+
if ((op === '*')) {
|
|
370
|
+
return ((a * b));
|
|
371
|
+
}
|
|
372
|
+
if ((op === '/')) {
|
|
373
|
+
if ((b !== 0)) {
|
|
374
|
+
return ((a / b));
|
|
375
|
+
} else {
|
|
376
|
+
return 0;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return b;
|
|
380
|
+
Object.assign(this, { value, operator, previousValue, display, isNewNumber });
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
let calc = new Calculator();
|
|
384
|
+
// [ThyScript] Server declaration
|
|
385
|
+
if (!__thy_express) throw new Error('[ThyScript] express is not installed. Run: npm install express');
|
|
386
|
+
__thy_app.use(require('express').json());
|
|
387
|
+
__thy_app.use(require('express').urlencoded({ extended: true }));
|
|
388
|
+
__thy_print('⚡ Starting ThyScript Calculator Web Server...');
|
|
389
|
+
const __thy_server = __thy_app.listen(3000, '0.0.0.0', () => {
|
|
390
|
+
__thy_print(`[ThyScript] Server running on http://${'0.0.0.0'}:${3000}`);
|
|
391
|
+
});
|
|
392
|
+
if (__thy_app) {
|
|
393
|
+
__thy_app.get('/', async (request, response) => {
|
|
394
|
+
const req = request; const res = response;
|
|
395
|
+
// HtmlClass: calculator_page
|
|
396
|
+
const __html_calculator_page = `<!DOCTYPE html>
|
|
397
|
+
<html lang="id">
|
|
398
|
+
<head>
|
|
399
|
+
<meta charset="UTF-8">
|
|
400
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
401
|
+
<title>ThyScript Calculator</title>
|
|
402
|
+
<style>
|
|
403
|
+
* { margin: 0; padding: 0; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
|
|
404
|
+
body {
|
|
405
|
+
background: radial-gradient(circle at 50% 20%, #1e1e38 0%, #0d0d1a 100%);
|
|
406
|
+
min-height: 100vh;
|
|
407
|
+
display: flex;
|
|
408
|
+
align-items: center;
|
|
409
|
+
justify-content: center;
|
|
410
|
+
color: #fff;
|
|
411
|
+
}
|
|
412
|
+
.calculator-card {
|
|
413
|
+
background: rgba(255, 255, 255, 0.05);
|
|
414
|
+
backdrop-filter: blur(25px);
|
|
415
|
+
-webkit-backdrop-filter: blur(25px);
|
|
416
|
+
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
417
|
+
border-radius: 28px;
|
|
418
|
+
padding: 28px;
|
|
419
|
+
width: 360px;
|
|
420
|
+
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
|
|
421
|
+
}
|
|
422
|
+
.badge {
|
|
423
|
+
display: inline-block;
|
|
424
|
+
background: linear-gradient(135deg, #6366f1, #a855f7);
|
|
425
|
+
color: white;
|
|
426
|
+
font-size: 11px;
|
|
427
|
+
font-weight: 700;
|
|
428
|
+
padding: 4px 10px;
|
|
429
|
+
border-radius: 20px;
|
|
430
|
+
margin-bottom: 16px;
|
|
431
|
+
letter-spacing: 1px;
|
|
432
|
+
}
|
|
433
|
+
.display-container {
|
|
434
|
+
background: rgba(0, 0, 0, 0.4);
|
|
435
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
436
|
+
border-radius: 18px;
|
|
437
|
+
padding: 20px;
|
|
438
|
+
text-align: right;
|
|
439
|
+
margin-bottom: 24px;
|
|
440
|
+
}
|
|
441
|
+
.history {
|
|
442
|
+
font-size: 14px;
|
|
443
|
+
color: rgba(255, 255, 255, 0.4);
|
|
444
|
+
min-height: 20px;
|
|
445
|
+
margin-bottom: 6px;
|
|
446
|
+
}
|
|
447
|
+
.current-value {
|
|
448
|
+
font-size: 38px;
|
|
449
|
+
font-weight: 300;
|
|
450
|
+
letter-spacing: 1px;
|
|
451
|
+
word-break: break-all;
|
|
452
|
+
}
|
|
453
|
+
.grid {
|
|
454
|
+
display: grid;
|
|
455
|
+
grid-template-columns: repeat(4, 1fr);
|
|
456
|
+
gap: 12px;
|
|
457
|
+
}
|
|
458
|
+
button {
|
|
459
|
+
border: none;
|
|
460
|
+
outline: none;
|
|
461
|
+
height: 60px;
|
|
462
|
+
border-radius: 16px;
|
|
463
|
+
font-size: 20px;
|
|
464
|
+
font-weight: 500;
|
|
465
|
+
cursor: pointer;
|
|
466
|
+
transition: all 0.15s ease;
|
|
467
|
+
display: flex;
|
|
468
|
+
align-items: center;
|
|
469
|
+
justify-content: center;
|
|
470
|
+
}
|
|
471
|
+
button:hover { transform: translateY(-2px); filter: brightness(1.2); }
|
|
472
|
+
button:active { transform: translateY(1px); }
|
|
473
|
+
.btn-num { background: rgba(255, 255, 255, 0.08); color: #fff; }
|
|
474
|
+
.btn-op { background: rgba(99, 102, 241, 0.25); color: #818cf8; }
|
|
475
|
+
.btn-action { background: rgba(239, 68, 68, 0.2); color: #f87171; }
|
|
476
|
+
.btn-equals {
|
|
477
|
+
grid-column: span 2;
|
|
478
|
+
background: linear-gradient(135deg, #6366f1, #a855f7);
|
|
479
|
+
color: white;
|
|
480
|
+
font-weight: 600;
|
|
481
|
+
}
|
|
482
|
+
.perf-badge {
|
|
483
|
+
text-align: center;
|
|
484
|
+
margin-top: 18px;
|
|
485
|
+
font-size: 12px;
|
|
486
|
+
color: #10b981;
|
|
487
|
+
}
|
|
488
|
+
</style>
|
|
489
|
+
</head>
|
|
490
|
+
<body>
|
|
491
|
+
<div class="calculator-card">
|
|
492
|
+
<div class="badge">⚡ THYSCRIPT JIT</div>
|
|
493
|
+
<div class="display-container">
|
|
494
|
+
<div class="history" id="historyText"></div>
|
|
495
|
+
<div class="current-value" id="displayText">0</div>
|
|
496
|
+
</div>
|
|
497
|
+
<div class="grid">
|
|
498
|
+
<button class="btn-action" onclick="handleClear()">C</button>
|
|
499
|
+
<button class="btn-op" onclick="handleOp('+/-')">±</button>
|
|
500
|
+
<button class="btn-op" onclick="handleOp('%')">%</button>
|
|
501
|
+
<button class="btn-op" onclick="handleOp('/')">÷</button>
|
|
502
|
+
|
|
503
|
+
<button class="btn-num" onclick="handleDigit('7')">7</button>
|
|
504
|
+
<button class="btn-num" onclick="handleDigit('8')">8</button>
|
|
505
|
+
<button class="btn-num" onclick="handleDigit('9')">9</button>
|
|
506
|
+
<button class="btn-op" onclick="handleOp('*')">×</button>
|
|
507
|
+
|
|
508
|
+
<button class="btn-num" onclick="handleDigit('4')">4</button>
|
|
509
|
+
<button class="btn-num" onclick="handleDigit('5')">5</button>
|
|
510
|
+
<button class="btn-num" onclick="handleDigit('6')">6</button>
|
|
511
|
+
<button class="btn-op" onclick="handleOp('-')">−</button>
|
|
512
|
+
|
|
513
|
+
<button class="btn-num" onclick="handleDigit('1')">1</button>
|
|
514
|
+
<button class="btn-num" onclick="handleDigit('2')">2</button>
|
|
515
|
+
<button class="btn-num" onclick="handleDigit('3')">3</button>
|
|
516
|
+
<button class="btn-op" onclick="handleOp('+')">+</button>
|
|
517
|
+
|
|
518
|
+
<button class="btn-num" onclick="handleDigit('0')">0</button>
|
|
519
|
+
<button class="btn-num" onclick="handleDigit('.')">.</button>
|
|
520
|
+
<button class="btn-equals" onclick="handleEquals()">=</button>
|
|
521
|
+
</div>
|
|
522
|
+
<div class="perf-badge">🚀 12x Accelerated Math Preprocessing</div>
|
|
523
|
+
</div>
|
|
524
|
+
|
|
525
|
+
<script>
|
|
526
|
+
let cur = '0';
|
|
527
|
+
let prev = null;
|
|
528
|
+
let op = null;
|
|
529
|
+
let reset = false;
|
|
530
|
+
|
|
531
|
+
const disp = document.getElementById('displayText');
|
|
532
|
+
const hist = document.getElementById('historyText');
|
|
533
|
+
|
|
534
|
+
function update() {
|
|
535
|
+
disp.innerText = cur;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function handleDigit(d) {
|
|
539
|
+
if (reset || cur === '0') {
|
|
540
|
+
cur = d === '.' ? '0.' : d;
|
|
541
|
+
reset = false;
|
|
542
|
+
} else {
|
|
543
|
+
if (d === '.' && cur.includes('.')) return;
|
|
544
|
+
cur += d;
|
|
545
|
+
}
|
|
546
|
+
update();
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function handleOp(nextOp) {
|
|
550
|
+
if (nextOp === '+/-') {
|
|
551
|
+
cur = String(-parseFloat(cur));
|
|
552
|
+
update();
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
if (nextOp === '%') {
|
|
556
|
+
cur = String(parseFloat(cur) / 100);
|
|
557
|
+
update();
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
if (op && !reset) {
|
|
561
|
+
calculate();
|
|
562
|
+
}
|
|
563
|
+
prev = parseFloat(cur);
|
|
564
|
+
op = nextOp;
|
|
565
|
+
reset = true;
|
|
566
|
+
hist.innerText = prev + ' ' + op;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function calculate() {
|
|
570
|
+
if (!op || prev === null) return;
|
|
571
|
+
const b = parseFloat(cur);
|
|
572
|
+
let res = 0;
|
|
573
|
+
if (op === '+') res = prev + b;
|
|
574
|
+
if (op === '-') res = prev - b;
|
|
575
|
+
if (op === '*') res = prev * b;
|
|
576
|
+
if (op === '/') res = b !== 0 ? prev / b : 'Error';
|
|
577
|
+
cur = String(res);
|
|
578
|
+
op = null;
|
|
579
|
+
prev = null;
|
|
580
|
+
reset = true;
|
|
581
|
+
hist.innerText = '';
|
|
582
|
+
update();
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function handleEquals() {
|
|
586
|
+
calculate();
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function handleClear() {
|
|
590
|
+
cur = '0';
|
|
591
|
+
prev = null;
|
|
592
|
+
op = null;
|
|
593
|
+
reset = false;
|
|
594
|
+
hist.innerText = '';
|
|
595
|
+
update();
|
|
596
|
+
}
|
|
597
|
+
</script>
|
|
598
|
+
</body>
|
|
599
|
+
</html>`;
|
|
600
|
+
if (typeof response !== 'undefined' && response && typeof response.send === 'function') {
|
|
601
|
+
response.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
602
|
+
response.send(__html_calculator_page);
|
|
603
|
+
} else if (typeof __thy_app !== 'undefined' && __thy_app) {
|
|
604
|
+
__thy_app.get('/calculator_page', (req, res) => {
|
|
605
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
606
|
+
res.send(__html_calculator_page);
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
if (__thy_app) {
|
|
612
|
+
__thy_app.get('/api/eval', async (request, response) => {
|
|
613
|
+
const req = request; const res = response;
|
|
614
|
+
let expr = request.query.expr;
|
|
615
|
+
if (expr) {
|
|
616
|
+
let result = (expr);
|
|
617
|
+
response.json({ 'success': true, 'result': result, 'poweredBy': 'ThyScript JIT' });
|
|
618
|
+
} else {
|
|
619
|
+
response.json({ 'success': false, 'message': 'No expression provided' });
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
__thy_print(('🚀 ThyScript Calculator application loaded successfully on port ' + port));
|
|
624
|
+
})().catch(e => { console.error("[ThyScript Runtime Error]", e); process.exit(1); });
|
package/examples/calculator.thy
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thyscript",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "ThyScript - A blazing-fast programming language built on Node.js
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "ThyScript v2.0 - A blazing-fast programming language built on Node.js with JIT compiler, native ML, physics simulation, full-stack web, remote control panel, and npm ecosystem integration",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"thy-node": "bin/thy-node.js",
|
package/public/home.html
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>{{title}}</title>
|
|
6
|
+
<style>
|
|
7
|
+
body { font-family: sans-serif; background: #0b0f19; color: #fff; padding: 40px; }
|
|
8
|
+
.card { background: #161f30; padding: 24px; border-radius: 12px; border: 1px solid #38bdf8; }
|
|
9
|
+
h1 { color: #38bdf8; }
|
|
10
|
+
</style>
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<div class="card">
|
|
14
|
+
<h1>{{title}}</h1>
|
|
15
|
+
<p>This is an external HTML file served by ThyScript v2.0!</p>
|
|
16
|
+
<p>User: <strong>{{user}}</strong></p>
|
|
17
|
+
</div>
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|