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
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "thyscript",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "ThyScript - A blazing-fast programming language built on Node.js for ML, physics simulation, web, game engine, full-stack, and complex algorithms",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"thy-node": "bin/thy-node.js",
|
|
8
|
+
"thy": "bin/thy.js",
|
|
9
|
+
"thys": "bin/thys.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "node tests/run-tests.js",
|
|
13
|
+
"build": "node scripts/build.js"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"thyscript",
|
|
17
|
+
"programming-language",
|
|
18
|
+
"jit-compiler",
|
|
19
|
+
"machine-learning",
|
|
20
|
+
"physics-simulation",
|
|
21
|
+
"web",
|
|
22
|
+
"full-stack",
|
|
23
|
+
"game-engine"
|
|
24
|
+
],
|
|
25
|
+
"author": "ThyScript Team",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"acorn": "^8.11.3",
|
|
29
|
+
"bcryptjs": "^3.0.3",
|
|
30
|
+
"better-sqlite3": "^13.0.3",
|
|
31
|
+
"boxen": "^5.1.2",
|
|
32
|
+
"chalk": "^4.1.2",
|
|
33
|
+
"chokidar": "^3.5.3",
|
|
34
|
+
"commander": "^11.1.0",
|
|
35
|
+
"compression": "^1.7.4",
|
|
36
|
+
"cors": "^2.8.5",
|
|
37
|
+
"dotenv": "^16.3.1",
|
|
38
|
+
"express": "^4.18.2",
|
|
39
|
+
"fast-json-stringify": "^5.9.1",
|
|
40
|
+
"figlet": "^1.7.0",
|
|
41
|
+
"jsonwebtoken": "^9.0.2",
|
|
42
|
+
"mathjs": "^12.2.1",
|
|
43
|
+
"node-cache": "^5.1.2",
|
|
44
|
+
"ora": "^5.4.1",
|
|
45
|
+
"socket.io": "^4.6.1",
|
|
46
|
+
"toml": "^3.0.0",
|
|
47
|
+
"uuid": "^9.0.1",
|
|
48
|
+
"ws": "^8.16.0",
|
|
49
|
+
"yaml": "^2.3.4"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=18.0.0"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const Thy = require('../src/index.js');
|
|
3
|
+
const src = `class A: end()
|
|
4
|
+
const table a = new A()
|
|
5
|
+
print(a instanceof A)
|
|
6
|
+
`;
|
|
7
|
+
(async () => {
|
|
8
|
+
try {
|
|
9
|
+
const lexer = new Thy.Lexer(src, '<t>');
|
|
10
|
+
const tokens = lexer.tokenize();
|
|
11
|
+
const parser = new Thy.Parser(tokens, src, '<t>');
|
|
12
|
+
const ast = parser.parse();
|
|
13
|
+
const c2 = new Thy.JITCompiler(ast, src, '<t>', {});
|
|
14
|
+
const compiled = c2.compile();
|
|
15
|
+
console.log('=== COMPILED ===');
|
|
16
|
+
console.log(compiled);
|
|
17
|
+
} catch (e) { console.log('ERR', e.message); } finally { process.exit(0); }
|
|
18
|
+
})();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const Thy = require('../src/index.js');
|
|
3
|
+
const src = `class A\nend()\nconst table a = new A()\n`;
|
|
4
|
+
try {
|
|
5
|
+
const lexer = new Thy.Lexer(src, '<t>');
|
|
6
|
+
const tokens = lexer.tokenize();
|
|
7
|
+
console.log('tokens:', tokens.map(t=>t.type).join(','));
|
|
8
|
+
const parser = new Thy.Parser(tokens, src, '<t>');
|
|
9
|
+
const ast = parser.parse();
|
|
10
|
+
console.log('AST types:', (ast.body||ast.statements||[]).map(s=>s.type).join(','));
|
|
11
|
+
} catch(e){ console.log('ERR', e.message); }
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const Thy = require('../src/index.js');
|
|
3
|
+
const src = `public class(A):\nend()\nconst table a = new A()\nprint(typeof a)\n`;
|
|
4
|
+
(async () => {
|
|
5
|
+
try {
|
|
6
|
+
const lexer = new Thy.Lexer(src, '<t>');
|
|
7
|
+
const tokens = lexer.tokenize();
|
|
8
|
+
const parser = new Thy.Parser(tokens, src, '<t>');
|
|
9
|
+
const ast = parser.parse();
|
|
10
|
+
console.log('AST types:', (ast.body||ast.statements||[]).map(s=>s.type).join(','));
|
|
11
|
+
const c2 = new Thy.JITCompiler(ast, src, '<t>', {});
|
|
12
|
+
const compiled = c2.compile();
|
|
13
|
+
const hasClass = compiled.includes('class A') || compiled.includes('class A ');
|
|
14
|
+
console.log('has class A decl:', hasClass);
|
|
15
|
+
const runtime = new Thy.ThyRuntime({});
|
|
16
|
+
await runtime.init();
|
|
17
|
+
const ctx = runtime.buildContext();
|
|
18
|
+
await Promise.race([c2.execute(compiled, ctx), new Promise((_,rej)=>setTimeout(()=>rej(new Error('TIMEOUT')),3000))]);
|
|
19
|
+
console.log('EXEC OK');
|
|
20
|
+
} catch (e) { console.log('ERR', e.message, e.stack && e.stack.split('\n')[1]); } finally { process.exit(0); }
|
|
21
|
+
})();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const Thy = require('../src/index.js');
|
|
3
|
+
const src = `const table [a, b] = [1, 2, 3]
|
|
4
|
+
print(a)
|
|
5
|
+
print(b)
|
|
6
|
+
`;
|
|
7
|
+
console.log('--- lexing ---');
|
|
8
|
+
try {
|
|
9
|
+
const lexer = new Thy.Lexer(src, '<t>');
|
|
10
|
+
const tokens = lexer.tokenize();
|
|
11
|
+
console.log('tokens:', tokens.map(t => t.type).join(','));
|
|
12
|
+
} catch (e) { console.log('LEX ERRR', e.message); }
|
|
13
|
+
console.log('--- parsing ---');
|
|
14
|
+
try {
|
|
15
|
+
const lexer = new Thy.Lexer(src, '<t>');
|
|
16
|
+
const tokens = lexer.tokenize();
|
|
17
|
+
const parser = new Thy.Parser(tokens, src, '<t>');
|
|
18
|
+
const ast = parser.parse();
|
|
19
|
+
console.log('parsed OK');
|
|
20
|
+
} catch (e) { console.log('PARSE ERR', e.message); }
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const Thy = require('../src/index.js');
|
|
3
|
+
const src = `const table [a, b] = [1, 2, 3]
|
|
4
|
+
print(a)
|
|
5
|
+
print(b)
|
|
6
|
+
`;
|
|
7
|
+
(async () => {
|
|
8
|
+
try {
|
|
9
|
+
const lexer = new Thy.Lexer(src, '<t>');
|
|
10
|
+
const tokens = lexer.tokenize();
|
|
11
|
+
const parser = new Thy.Parser(tokens, src, '<t>');
|
|
12
|
+
const ast = parser.parse();
|
|
13
|
+
const c2 = new Thy.JITCompiler(ast, src, '<t>', {});
|
|
14
|
+
const compiled = c2.compile();
|
|
15
|
+
console.log('COMPILED:\n', compiled.split('\n').filter(l => l.includes('a')||l.includes('[')).slice(0,9).join('\n'));
|
|
16
|
+
const runtime = new Thy.ThyRuntime({});
|
|
17
|
+
await runtime.init();
|
|
18
|
+
const ctx = runtime.buildContext();
|
|
19
|
+
const r = await Promise.race([c2.execute(compiled, ctx), new Promise((_,rej)=>setTimeout(()=>rej(new Error('TIMEOUT')),3000))]);
|
|
20
|
+
console.log('EXEC OK', r);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
console.log('ERR', e.message);
|
|
23
|
+
} finally { process.exit(0); }
|
|
24
|
+
})();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const t = require('../src/index.js');
|
|
3
|
+
const src = `program MyApp {
|
|
4
|
+
start {
|
|
5
|
+
print("Hello")
|
|
6
|
+
let x: int = 42
|
|
7
|
+
print(x)
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
try {
|
|
12
|
+
const js = t.compile(src, 't.thy', {});
|
|
13
|
+
console.log('scaffold COMPILES, bytes=', js.length);
|
|
14
|
+
} catch (e) {
|
|
15
|
+
console.log('scaffold FAIL:', e.message);
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const t = require('../src/index.js');
|
|
3
|
+
const src = `program MyApp {
|
|
4
|
+
start {
|
|
5
|
+
print("HelloFromScaffold")
|
|
6
|
+
let x = 42
|
|
7
|
+
print(x)
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
try {
|
|
12
|
+
const js = t.compile(src, 't.thy', {});
|
|
13
|
+
const body = js.split('// ── program body')[1] || js;
|
|
14
|
+
console.log('BODY HAS print:', body.includes('print("HelloFromScaffold")'));
|
|
15
|
+
console.log('BODY snippet:', body.slice(0, 300));
|
|
16
|
+
} catch (e) { console.log('FAIL', e.message); }
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const t = require('../src/index.js');
|
|
3
|
+
const src = `program MyApp {
|
|
4
|
+
start {
|
|
5
|
+
print("HelloFromScaffold")
|
|
6
|
+
let x = 42
|
|
7
|
+
print(x)
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
(async () => {
|
|
12
|
+
try {
|
|
13
|
+
const js = t.compile(src, 't.thy', {});
|
|
14
|
+
const runtime = new t.ThyRuntime({});
|
|
15
|
+
await runtime.init();
|
|
16
|
+
const lexer = new t.Lexer(src, 't.thy');
|
|
17
|
+
const tokens = lexer.tokenize();
|
|
18
|
+
const parser = new t.Parser(tokens, src, 't.thy');
|
|
19
|
+
const ast = parser.parse();
|
|
20
|
+
const c2 = new t.JITCompiler(ast, src, 't.thy', {});
|
|
21
|
+
const compiled = c2.compile();
|
|
22
|
+
const ctx = runtime.buildContext();
|
|
23
|
+
await Promise.race([c2.execute(compiled, ctx), new Promise((_, r) => setTimeout(() => r(new Error('TIMEOUT')), 3000))]);
|
|
24
|
+
console.log('EXEC OK');
|
|
25
|
+
} catch (e) { console.log('ERR', e.message); } finally { process.exit(0); }
|
|
26
|
+
})();
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const Thy = require('../src/index.js');
|
|
2
|
+
const src = `const table a = "Hello"
|
|
3
|
+
const table b = "World"
|
|
4
|
+
print(\`Template ${'$'}{a}-${'$'}{b}\`)
|
|
5
|
+
`;
|
|
6
|
+
try {
|
|
7
|
+
const js = Thy.compile(src, 'tpl.thy', {});
|
|
8
|
+
// Find the template literal line
|
|
9
|
+
const lines = js.split('\n');
|
|
10
|
+
lines.forEach((l, i) => { if (l.includes('`')) console.log('LINE', i, JSON.stringify(l)); });
|
|
11
|
+
} catch (e) {
|
|
12
|
+
console.log('COMPILE ERR:', e.message);
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const L = require('../src/lexer/Lexer');
|
|
2
|
+
const src = `const table a = "Hello"
|
|
3
|
+
const table b = "World"
|
|
4
|
+
print(\`Template ${'$'}{a}-${'$'}{b}\`)
|
|
5
|
+
print(\`val=${'$'}{1+2}\`)
|
|
6
|
+
`;
|
|
7
|
+
const l = new L(src);
|
|
8
|
+
const toks = l.tokenize();
|
|
9
|
+
console.log(JSON.stringify(toks.filter(t => t.type === 'TEMPLATE_LITERAL'), null, 2));
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const Thy = require('../src/index.js');
|
|
3
|
+
const src = `const table x = 5
|
|
4
|
+
print(typeof x)
|
|
5
|
+
class A: end()
|
|
6
|
+
const table a = new A()
|
|
7
|
+
print(a instanceof A)
|
|
8
|
+
`;
|
|
9
|
+
(async () => {
|
|
10
|
+
try {
|
|
11
|
+
const lexer = new Thy.Lexer(src, '<t>');
|
|
12
|
+
const tokens = lexer.tokenize();
|
|
13
|
+
const parser = new Thy.Parser(tokens, src, '<t>');
|
|
14
|
+
const ast = parser.parse();
|
|
15
|
+
const c2 = new Thy.JITCompiler(ast, src, '<t>', {});
|
|
16
|
+
const compiled = c2.compile();
|
|
17
|
+
console.log('COMPILED tail:\n' + compiled.split('\n').slice(-25).join('\n'));
|
|
18
|
+
const runtime = new Thy.ThyRuntime({});
|
|
19
|
+
await runtime.init();
|
|
20
|
+
const ctx = runtime.buildContext();
|
|
21
|
+
const r = await Promise.race([c2.execute(compiled, ctx), new Promise((_, rej) => setTimeout(() => rej(new Error('TIMEOUT')), 3000))]);
|
|
22
|
+
console.log('EXEC OK', r);
|
|
23
|
+
} catch (e) {
|
|
24
|
+
console.log('ERR', e.message, '\n', e.stack && e.stack.split('\n').slice(0,3).join('\n'));
|
|
25
|
+
} finally { process.exit(0); }
|
|
26
|
+
})();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const Thy = require('../src/index.js');
|
|
3
|
+
const src = `const table x = 5
|
|
4
|
+
print(typeof x)
|
|
5
|
+
public class A:
|
|
6
|
+
end()
|
|
7
|
+
const table a = new A()
|
|
8
|
+
print(a instanceof A)
|
|
9
|
+
`;
|
|
10
|
+
(async () => {
|
|
11
|
+
try {
|
|
12
|
+
const lexer = new Thy.Lexer(src, '<t>');
|
|
13
|
+
const tokens = lexer.tokenize();
|
|
14
|
+
const parser = new Thy.Parser(tokens, src, '<t>');
|
|
15
|
+
const ast = parser.parse();
|
|
16
|
+
const c2 = new Thy.JITCompiler(ast, src, '<t>', {});
|
|
17
|
+
const compiled = c2.compile();
|
|
18
|
+
console.log('has class:', compiled.includes('class A'));
|
|
19
|
+
const runtime = new Thy.ThyRuntime({});
|
|
20
|
+
await runtime.init();
|
|
21
|
+
const ctx = runtime.buildContext();
|
|
22
|
+
const r = await Promise.race([c2.execute(compiled, ctx), new Promise((_, rej) => setTimeout(() => rej(new Error('TIMEOUT')), 3000))]);
|
|
23
|
+
console.log('EXEC OK');
|
|
24
|
+
} catch (e) { console.log('ERR', e.message); } finally { process.exit(0); }
|
|
25
|
+
})();
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
let code = fs.readFileSync('src/compiler/CodeGenerator.js', 'utf8');
|
|
3
|
+
|
|
4
|
+
// Find the old block by index
|
|
5
|
+
const marker = 'if (!isStatic && classProps.length > 0) {';
|
|
6
|
+
const markerIdx = code.indexOf(marker);
|
|
7
|
+
if (markerIdx === -1) {
|
|
8
|
+
console.error('Marker not found!');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Find end of the whole _genMethod closing brace after marker
|
|
13
|
+
const blockEnd = code.indexOf('\n }\n\n // ─── HTML class', markerIdx);
|
|
14
|
+
if (blockEnd === -1) {
|
|
15
|
+
console.error('End marker not found!');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const before = code.slice(0, markerIdx);
|
|
20
|
+
const after = code.slice(blockEnd); // includes '\n }\n\n // ─── HTML class...'
|
|
21
|
+
|
|
22
|
+
const newBlock = `if (!isStatic && classProps.length > 0) {
|
|
23
|
+
// Destructure class props as mutable lets (allows += inside methods)
|
|
24
|
+
this._emit('let { ' + classProps.join(', ') + ' } = this;');
|
|
25
|
+
this._genBlockOrList(method.body);
|
|
26
|
+
// Write any mutations back to the instance
|
|
27
|
+
this._emit('Object.assign(this, { ' + classProps.join(', ') + ' });');
|
|
28
|
+
} else {
|
|
29
|
+
this._genBlockOrList(method.body);
|
|
30
|
+
}
|
|
31
|
+
this._pop();
|
|
32
|
+
this._emit('}');
|
|
33
|
+
}`;
|
|
34
|
+
|
|
35
|
+
const newCode = before + newBlock + after;
|
|
36
|
+
fs.writeFileSync('src/compiler/CodeGenerator.js', newCode);
|
|
37
|
+
|
|
38
|
+
// Verify
|
|
39
|
+
const verify = fs.readFileSync('src/compiler/CodeGenerator.js', 'utf8');
|
|
40
|
+
if (verify.includes('Destructure class props as mutable lets')) {
|
|
41
|
+
console.log('SUCCESS: _genMethod patched correctly!');
|
|
42
|
+
} else {
|
|
43
|
+
console.error('FAIL: patch not applied');
|
|
44
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* Smoke test harness - exercises many ThyScript language features
|
|
4
|
+
* to uncover crashes, undefined behavior, and anomalies.
|
|
5
|
+
*/
|
|
6
|
+
const Thy = require('../src/index.js');
|
|
7
|
+
|
|
8
|
+
const cases = {
|
|
9
|
+
'basic-assign': `
|
|
10
|
+
const table x = 10
|
|
11
|
+
print(x)
|
|
12
|
+
const table y = x + 5 * 2
|
|
13
|
+
print(y)
|
|
14
|
+
`,
|
|
15
|
+
'string-concat': `
|
|
16
|
+
const table a = "Hello"
|
|
17
|
+
const table b = "World"
|
|
18
|
+
print(a + " " + b)
|
|
19
|
+
print(\`Template ${'$'}{a}-${'$'}{b}\`)
|
|
20
|
+
`,
|
|
21
|
+
'if-elif-else': `
|
|
22
|
+
const table v = 42
|
|
23
|
+
if(v < 10):
|
|
24
|
+
print("small")
|
|
25
|
+
elif(v < 50):
|
|
26
|
+
print("medium")
|
|
27
|
+
else:
|
|
28
|
+
print("large")
|
|
29
|
+
end()
|
|
30
|
+
`,
|
|
31
|
+
'while-loop': `
|
|
32
|
+
const table i = 0
|
|
33
|
+
const table s = 0
|
|
34
|
+
while(i < 10):
|
|
35
|
+
add i in s
|
|
36
|
+
add 1 in i
|
|
37
|
+
end()
|
|
38
|
+
print(s)
|
|
39
|
+
`,
|
|
40
|
+
'for-loop': `
|
|
41
|
+
const table arr = [1, 2, 3, 4]
|
|
42
|
+
const table out = 0
|
|
43
|
+
for item in arr:
|
|
44
|
+
add item in out
|
|
45
|
+
end()
|
|
46
|
+
print(out)
|
|
47
|
+
`,
|
|
48
|
+
'functions': `
|
|
49
|
+
func square(x):
|
|
50
|
+
return calculate(x * x)
|
|
51
|
+
end()
|
|
52
|
+
print(square(5))
|
|
53
|
+
`,
|
|
54
|
+
'object-literal': `
|
|
55
|
+
const table obj = {name: "Budi", age: 25}
|
|
56
|
+
print(obj.name)
|
|
57
|
+
print(obj["name"])
|
|
58
|
+
`,
|
|
59
|
+
'prime-check': `
|
|
60
|
+
func isPrime(n):
|
|
61
|
+
if(n <= 1):
|
|
62
|
+
return false
|
|
63
|
+
end()
|
|
64
|
+
const table i = 2
|
|
65
|
+
while(i * i <= n):
|
|
66
|
+
if(n % i == 0):
|
|
67
|
+
return false
|
|
68
|
+
end()
|
|
69
|
+
add 1 in i
|
|
70
|
+
end()
|
|
71
|
+
return true
|
|
72
|
+
end()
|
|
73
|
+
print(isPrime(7))
|
|
74
|
+
print(isPrime(10))
|
|
75
|
+
`,
|
|
76
|
+
'switch': `
|
|
77
|
+
const table day = 2
|
|
78
|
+
switch(day):
|
|
79
|
+
case 1:
|
|
80
|
+
print("Monday")
|
|
81
|
+
case 2:
|
|
82
|
+
print("Tuesday")
|
|
83
|
+
default:
|
|
84
|
+
print("Other")
|
|
85
|
+
end()
|
|
86
|
+
`,
|
|
87
|
+
'try-catch': `
|
|
88
|
+
try:
|
|
89
|
+
const table z = 10 / 0
|
|
90
|
+
print("no error")
|
|
91
|
+
except e:
|
|
92
|
+
print("caught: " + e)
|
|
93
|
+
end()
|
|
94
|
+
`,
|
|
95
|
+
'ternary': `
|
|
96
|
+
const table age = 17
|
|
97
|
+
print(age >= 18 ? "adult" : "minor")
|
|
98
|
+
`,
|
|
99
|
+
'string-methods': `
|
|
100
|
+
const table s = " Hello World "
|
|
101
|
+
print(string.trim(s).length)
|
|
102
|
+
print(string.upper(s))
|
|
103
|
+
`,
|
|
104
|
+
'array-methods': `
|
|
105
|
+
const table nums = [3, 1, 2]
|
|
106
|
+
print(array.sort(nums).join(","))
|
|
107
|
+
`,
|
|
108
|
+
'nested-loop': `
|
|
109
|
+
const table total = 0
|
|
110
|
+
const table i = 0
|
|
111
|
+
while(i < 3):
|
|
112
|
+
const table j = 0
|
|
113
|
+
while(j < 3):
|
|
114
|
+
add (i * j) in total
|
|
115
|
+
add 1 in j
|
|
116
|
+
end()
|
|
117
|
+
add 1 in i
|
|
118
|
+
end()
|
|
119
|
+
print(total)
|
|
120
|
+
`,
|
|
121
|
+
'class-basic': `
|
|
122
|
+
public class Animal:
|
|
123
|
+
public name = "unknown"
|
|
124
|
+
public func speak():
|
|
125
|
+
return "..." + this.name
|
|
126
|
+
end()
|
|
127
|
+
public func setName(n):
|
|
128
|
+
change(this.name to(n))
|
|
129
|
+
end()
|
|
130
|
+
end()
|
|
131
|
+
const table a = new Animal()
|
|
132
|
+
a.setName("Kucing")
|
|
133
|
+
print(a.speak())
|
|
134
|
+
`,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
async function main() {
|
|
138
|
+
let pass = 0, fail = 0;
|
|
139
|
+
for (const [name, src] of Object.entries(cases)) {
|
|
140
|
+
try {
|
|
141
|
+
// Compile
|
|
142
|
+
const js = Thy.compile(src, `<${name}>`, {});
|
|
143
|
+
// Execute
|
|
144
|
+
const runtime = new Thy.ThyRuntime({ enableClustering: false });
|
|
145
|
+
await runtime.init();
|
|
146
|
+
const context = runtime.buildContext();
|
|
147
|
+
const compiler = new Thy.JITCompiler(Thy.astFromSource ? null : null, src);
|
|
148
|
+
// Recompile via runSource-like path
|
|
149
|
+
const lexer = new Thy.Lexer(src, `<${name}>`);
|
|
150
|
+
const tokens = lexer.tokenize();
|
|
151
|
+
const parser = new Thy.Parser(tokens, src, `<${name}>`);
|
|
152
|
+
const ast = parser.parse();
|
|
153
|
+
const c2 = new Thy.JITCompiler(ast, src, `<${name}>`, {});
|
|
154
|
+
const compiled = c2.compile();
|
|
155
|
+
await c2.execute(compiled, context);
|
|
156
|
+
console.log(`PASS ${name}`);
|
|
157
|
+
pass++;
|
|
158
|
+
} catch (e) {
|
|
159
|
+
console.log(`FAIL ${name}: ${e.message}`);
|
|
160
|
+
fail++;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
console.log(`\n=== Smoke: ${pass} passed, ${fail} failed ===`);
|
|
164
|
+
process.exit(fail ? 1 : 0);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
main().catch(e => { console.error('FATAL', e); process.exit(1); });
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const Thy = require('../src/index.js');
|
|
3
|
+
|
|
4
|
+
const cases = {
|
|
5
|
+
'class-inheritance': `
|
|
6
|
+
public class Animal:
|
|
7
|
+
public name = "?"
|
|
8
|
+
public func speak():
|
|
9
|
+
return "sound"
|
|
10
|
+
end()
|
|
11
|
+
public func setName(n):
|
|
12
|
+
change(this.name to(n))
|
|
13
|
+
end()
|
|
14
|
+
end()
|
|
15
|
+
public class Dog extends Animal:
|
|
16
|
+
public func speak():
|
|
17
|
+
return "Woof"
|
|
18
|
+
end()
|
|
19
|
+
end()
|
|
20
|
+
const table d = new Dog()
|
|
21
|
+
d.setName("Rex")
|
|
22
|
+
print(d.name)
|
|
23
|
+
print(d.speak())
|
|
24
|
+
`,
|
|
25
|
+
'class-inheritance-super': `
|
|
26
|
+
public class Base:
|
|
27
|
+
public value = 10
|
|
28
|
+
public func get():
|
|
29
|
+
return this.value
|
|
30
|
+
end()
|
|
31
|
+
end()
|
|
32
|
+
public class Child extends Base:
|
|
33
|
+
public func get():
|
|
34
|
+
return this.value * 2
|
|
35
|
+
end()
|
|
36
|
+
end()
|
|
37
|
+
const table c = new Child()
|
|
38
|
+
print(c.get())
|
|
39
|
+
`,
|
|
40
|
+
'async-func': `
|
|
41
|
+
async func fetchVal():
|
|
42
|
+
return 42
|
|
43
|
+
end()
|
|
44
|
+
print(fetchVal())
|
|
45
|
+
`,
|
|
46
|
+
'destructure-array': `
|
|
47
|
+
const table [a, b] = [1, 2, 3]
|
|
48
|
+
print(a)
|
|
49
|
+
print(b)
|
|
50
|
+
`,
|
|
51
|
+
'destructure-object': `
|
|
52
|
+
const table {x, y} = {x: 5, y: 7}
|
|
53
|
+
print(x)
|
|
54
|
+
print(y)
|
|
55
|
+
`,
|
|
56
|
+
'spread': `
|
|
57
|
+
const table a = [1, 2]
|
|
58
|
+
const table b = [...a, 3, 4]
|
|
59
|
+
print(b.length)
|
|
60
|
+
print(b.join(','))
|
|
61
|
+
`,
|
|
62
|
+
'nested-object-access': `
|
|
63
|
+
const table user = {profile: {name: "Budi", address: {city: "Jakarta"}}}
|
|
64
|
+
print(user.profile.address.city)
|
|
65
|
+
print(user.profile["name"])
|
|
66
|
+
`,
|
|
67
|
+
'js-array-methods': `
|
|
68
|
+
const table nums = [5, 3, 8, 1]
|
|
69
|
+
print(nums.sort((a,b) => a-b).join(','))
|
|
70
|
+
print(nums.map(x => x * 2).join(','))
|
|
71
|
+
print(nums.filter(x => x > 3).join(','))
|
|
72
|
+
`,
|
|
73
|
+
'string-template-nested': `
|
|
74
|
+
const table name = "World"
|
|
75
|
+
const table greeting = \`Hello ${'$'}{name}, sum=${'$'}{1+2}\`
|
|
76
|
+
print(greeting)
|
|
77
|
+
`,
|
|
78
|
+
'modulo-power': `
|
|
79
|
+
print(calculate(5 ** 2))
|
|
80
|
+
print(calculate(10 % 3))
|
|
81
|
+
print(calculate(2 ** 3 ** 2))
|
|
82
|
+
`,
|
|
83
|
+
'object-in-array': `
|
|
84
|
+
const table items = [{id: 1, name: "A"}, {id: 2, name: "B"}]
|
|
85
|
+
print(items.length)
|
|
86
|
+
print(items[1].name)
|
|
87
|
+
`,
|
|
88
|
+
'whilenested-if': `
|
|
89
|
+
const table out = 0
|
|
90
|
+
const table i = 0
|
|
91
|
+
while(i < 10):
|
|
92
|
+
if(i % 2 == 0):
|
|
93
|
+
add i in out
|
|
94
|
+
end()
|
|
95
|
+
add 1 in i
|
|
96
|
+
end()
|
|
97
|
+
print(out)
|
|
98
|
+
`,
|
|
99
|
+
'null-coalesce': `
|
|
100
|
+
const table a = null
|
|
101
|
+
const table b = a ?? "default"
|
|
102
|
+
print(b)
|
|
103
|
+
`,
|
|
104
|
+
'typeof-instanceof': `
|
|
105
|
+
const table x = 5
|
|
106
|
+
print(typeof x)
|
|
107
|
+
public class(A):
|
|
108
|
+
end()
|
|
109
|
+
const table a = new A()
|
|
110
|
+
print(a instanceof A)
|
|
111
|
+
`,
|
|
112
|
+
'string-escape': `
|
|
113
|
+
print("line1\\nline2")
|
|
114
|
+
print("tab\\there")
|
|
115
|
+
print("quote\\" and \\'")
|
|
116
|
+
`,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
async function main() {
|
|
120
|
+
let pass = 0, fail = 0;
|
|
121
|
+
// Prevent compiled programs' .catch(process.exit) from killing the harness
|
|
122
|
+
const origExit = process.exit;
|
|
123
|
+
process.exit = () => { throw new Error('process.exit called by user program'); };
|
|
124
|
+
try {
|
|
125
|
+
for (const [name, src] of Object.entries(cases)) {
|
|
126
|
+
try {
|
|
127
|
+
const runtime = new Thy.ThyRuntime({ enableClustering: false });
|
|
128
|
+
await runtime.init();
|
|
129
|
+
const lexer = new Thy.Lexer(src, `<${name}>`);
|
|
130
|
+
const tokens = lexer.tokenize();
|
|
131
|
+
const parser = new Thy.Parser(tokens, src, `<${name}>`);
|
|
132
|
+
const ast = parser.parse();
|
|
133
|
+
const c2 = new Thy.JITCompiler(ast, src, `<${name}>`, {});
|
|
134
|
+
const compiled = c2.compile();
|
|
135
|
+
const context = runtime.buildContext();
|
|
136
|
+
await c2.execute(compiled, context);
|
|
137
|
+
console.log(`PASS ${name}`);
|
|
138
|
+
pass++;
|
|
139
|
+
} catch (e) {
|
|
140
|
+
console.log(`FAIL ${name}: ${e.message}`);
|
|
141
|
+
fail++;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} finally {
|
|
145
|
+
process.exit = origExit;
|
|
146
|
+
}
|
|
147
|
+
console.log(`\n=== Extended Smoke: ${pass} passed, ${fail} failed ===`);
|
|
148
|
+
process.exit(fail ? 1 : 0);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
main().catch(e => { console.error('FATAL', e); process.exit(1); });
|