lt-script 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/LICENSE +21 -0
- package/README.md +33 -0
- package/dist/cli/ltc.d.ts +2 -0
- package/dist/cli/ltc.js +134 -0
- package/dist/cli/utils.d.ts +2 -0
- package/dist/cli/utils.js +26 -0
- package/dist/cli/watch.d.ts +1 -0
- package/dist/cli/watch.js +42 -0
- package/dist/compiler/codegen/LuaEmitter.d.ts +59 -0
- package/dist/compiler/codegen/LuaEmitter.js +748 -0
- package/dist/compiler/lexer/Lexer.d.ts +31 -0
- package/dist/compiler/lexer/Lexer.js +322 -0
- package/dist/compiler/lexer/Token.d.ts +109 -0
- package/dist/compiler/lexer/Token.js +169 -0
- package/dist/compiler/parser/AST.d.ts +314 -0
- package/dist/compiler/parser/AST.js +60 -0
- package/dist/compiler/parser/Parser.d.ts +68 -0
- package/dist/compiler/parser/Parser.js +873 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +12 -0
- package/package.json +33 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 LT Language Authors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# LT Language Compiler
|
|
2
|
+
|
|
3
|
+
A compiler for the LT language, targeting FiveM Lua.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g lt-compiler
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
ltc help
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Compiler
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
ltc build <sourceDir> <outDir>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Watch Mode
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
ltc watch <sourceDir> <outDir>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
- Transpiles LT to Lua 5.4 (FiveM compatible)
|
|
32
|
+
- Supports static typing syntax (transpiled to comments or checked)
|
|
33
|
+
- FiveM native function support
|
package/dist/cli/ltc.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { compile } from '../index.js';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { getAllFiles, ensureDirectoryExistence } from './utils.js';
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const command = args[0];
|
|
8
|
+
if (!command) {
|
|
9
|
+
printUsage();
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
switch (command) {
|
|
13
|
+
case 'watch':
|
|
14
|
+
handleWatch(args.slice(1));
|
|
15
|
+
break;
|
|
16
|
+
case 'build':
|
|
17
|
+
handleBuild(args.slice(1));
|
|
18
|
+
break;
|
|
19
|
+
default:
|
|
20
|
+
console.error(`Unknown command: ${command}`);
|
|
21
|
+
printUsage();
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
function printUsage() {
|
|
25
|
+
console.log(`
|
|
26
|
+
Usage:
|
|
27
|
+
ltc watch <targetDir> # Watches <targetDir>/src and builds to <targetDir>/build
|
|
28
|
+
ltc watch <sourceDir> <outDir> # Watches <sourceDir> and builds to <outDir>
|
|
29
|
+
ltc build <targetDir> # Builds <targetDir>/src to <targetDir>/build once
|
|
30
|
+
ltc build <sourceDir> <outDir> # Builds <sourceDir> to <outDir> once
|
|
31
|
+
`);
|
|
32
|
+
}
|
|
33
|
+
function parsePaths(cmdArgs) {
|
|
34
|
+
if (cmdArgs.length === 0)
|
|
35
|
+
return null;
|
|
36
|
+
let srcDir = '';
|
|
37
|
+
let outDir = '';
|
|
38
|
+
const target = cmdArgs[0];
|
|
39
|
+
if (cmdArgs.length === 1) {
|
|
40
|
+
// Convention mode: ltc watch lt-fuel
|
|
41
|
+
const potentialSrc = path.join(target, 'src');
|
|
42
|
+
if (fs.existsSync(potentialSrc) && fs.statSync(potentialSrc).isDirectory()) {
|
|
43
|
+
srcDir = potentialSrc;
|
|
44
|
+
outDir = path.join(target, 'build');
|
|
45
|
+
console.log(`[LT] Auto-detected structure. Source: ${srcDir}, Output: ${outDir}`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// Fallback? Or strict?
|
|
49
|
+
// If just passing a folder that HAS .lt files but no src/ folder?
|
|
50
|
+
// Let's assume strict src/build convention for single argument or error
|
|
51
|
+
console.error(`[LT] Error: Could not find 'src' directory in '${target}'.`);
|
|
52
|
+
console.error(` Please use 'ltc ${command} <sourceDir> <outDir>' for explicit paths.`);
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
// Explicit mode
|
|
58
|
+
srcDir = cmdArgs[0];
|
|
59
|
+
outDir = cmdArgs[1];
|
|
60
|
+
}
|
|
61
|
+
// Resolve to absolute
|
|
62
|
+
srcDir = path.resolve(srcDir);
|
|
63
|
+
outDir = path.resolve(outDir);
|
|
64
|
+
if (!fs.existsSync(srcDir)) {
|
|
65
|
+
console.error(`[LT] Error: Source directory '${srcDir}' does not exist.`);
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
return { srcDir, outDir };
|
|
69
|
+
}
|
|
70
|
+
function compileFile(srcPath, srcRoot, outRoot) {
|
|
71
|
+
try {
|
|
72
|
+
const relative = path.relative(srcRoot, srcPath);
|
|
73
|
+
const outPath = path.join(outRoot, relative.replace(/\.lt$/, '.lua'));
|
|
74
|
+
console.log(`[LT] Compiling: ${relative}`);
|
|
75
|
+
const content = fs.readFileSync(srcPath, 'utf-8');
|
|
76
|
+
const start = performance.now();
|
|
77
|
+
const lua = compile(content);
|
|
78
|
+
const end = performance.now();
|
|
79
|
+
ensureDirectoryExistence(outPath);
|
|
80
|
+
fs.writeFileSync(outPath, lua, 'utf8');
|
|
81
|
+
console.log(`[LT] ✓ Success (${(end - start).toFixed(2)}ms)`);
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
console.error(`[LT] ✗ Error compiling ${srcPath}:`);
|
|
85
|
+
console.error(e.message);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function handleBuild(cmdArgs) {
|
|
89
|
+
const paths = parsePaths(cmdArgs);
|
|
90
|
+
if (!paths)
|
|
91
|
+
process.exit(1);
|
|
92
|
+
const { srcDir, outDir } = paths;
|
|
93
|
+
console.log(`[LT] Building from '${srcDir}' to '${outDir}'...`);
|
|
94
|
+
const files = getAllFiles(srcDir, '.lt');
|
|
95
|
+
files.forEach(file => compileFile(file, srcDir, outDir));
|
|
96
|
+
console.log(`[LT] Build complete. ${files.length} files processed.`);
|
|
97
|
+
}
|
|
98
|
+
function handleWatch(cmdArgs) {
|
|
99
|
+
const paths = parsePaths(cmdArgs);
|
|
100
|
+
if (!paths)
|
|
101
|
+
process.exit(1);
|
|
102
|
+
const { srcDir, outDir } = paths;
|
|
103
|
+
// Initial build
|
|
104
|
+
handleBuild(cmdArgs);
|
|
105
|
+
console.log(`[LT] Watching for changes in '${srcDir}'...`);
|
|
106
|
+
let fsWait = null;
|
|
107
|
+
// Recursive watch
|
|
108
|
+
fs.watch(srcDir, { recursive: true }, (event, filename) => {
|
|
109
|
+
if (!filename)
|
|
110
|
+
return;
|
|
111
|
+
// Windows/Mac returning filename
|
|
112
|
+
if (!filename.endsWith('.lt'))
|
|
113
|
+
return; // Ignore non-lt files
|
|
114
|
+
const fullPath = path.join(srcDir, filename);
|
|
115
|
+
if (fsWait)
|
|
116
|
+
clearTimeout(fsWait);
|
|
117
|
+
fsWait = setTimeout(() => {
|
|
118
|
+
fsWait = null;
|
|
119
|
+
if (fs.existsSync(fullPath)) {
|
|
120
|
+
// Modified or Created
|
|
121
|
+
compileFile(fullPath, srcDir, outDir);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
// Deleted
|
|
125
|
+
const relative = path.relative(srcDir, fullPath);
|
|
126
|
+
const outPath = path.join(outDir, relative.replace(/\.lt$/, '.lua'));
|
|
127
|
+
if (fs.existsSync(outPath)) {
|
|
128
|
+
fs.unlinkSync(outPath);
|
|
129
|
+
console.log(`[LT] Removed: ${relative.replace(/\.lt$/, '.lua')}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}, 100); // 100ms debounce
|
|
133
|
+
});
|
|
134
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
export function getAllFiles(dir, extension, fileList = []) {
|
|
4
|
+
const files = fs.readdirSync(dir);
|
|
5
|
+
files.forEach(file => {
|
|
6
|
+
const filePath = path.join(dir, file);
|
|
7
|
+
const stat = fs.statSync(filePath);
|
|
8
|
+
if (stat.isDirectory()) {
|
|
9
|
+
getAllFiles(filePath, extension, fileList);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
if (path.extname(file) === extension) {
|
|
13
|
+
fileList.push(filePath);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
return fileList;
|
|
18
|
+
}
|
|
19
|
+
export function ensureDirectoryExistence(filePath) {
|
|
20
|
+
const dirname = path.dirname(filePath);
|
|
21
|
+
if (fs.existsSync(dirname)) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
ensureDirectoryExistence(dirname);
|
|
25
|
+
fs.mkdirSync(dirname);
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { compile } from '../index.js';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
const args = process.argv.slice(2);
|
|
4
|
+
if (args.length === 0) {
|
|
5
|
+
console.error("Usage: node watch.js <file.lt>");
|
|
6
|
+
process.exit(1);
|
|
7
|
+
}
|
|
8
|
+
const sourcePath = args[0];
|
|
9
|
+
const targetPath = sourcePath.replace(/\.lt$/, '.lua');
|
|
10
|
+
if (!fs.existsSync(sourcePath)) {
|
|
11
|
+
console.error(`Error: File '${sourcePath}' not found.`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
console.log(`[LT] Watching '${sourcePath}'...`);
|
|
15
|
+
function recompile() {
|
|
16
|
+
try {
|
|
17
|
+
console.log(`[LT] File changed. Compiling...`);
|
|
18
|
+
const content = fs.readFileSync(sourcePath, 'utf-8');
|
|
19
|
+
const start = performance.now();
|
|
20
|
+
const lua = compile(content);
|
|
21
|
+
const end = performance.now();
|
|
22
|
+
fs.writeFileSync(targetPath, lua, 'utf8');
|
|
23
|
+
console.log(`[LT] ✓ Success! (${(end - start).toFixed(2)}ms) -> ${targetPath}`);
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
console.error(`[LT] ✗ Error: ${e.message}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Initial compile
|
|
30
|
+
recompile();
|
|
31
|
+
// Watch for changes with debouncing and error recovery
|
|
32
|
+
let fsWait = null;
|
|
33
|
+
fs.watch(sourcePath, (event) => {
|
|
34
|
+
if (event === 'change') {
|
|
35
|
+
if (fsWait)
|
|
36
|
+
clearTimeout(fsWait);
|
|
37
|
+
fsWait = setTimeout(() => {
|
|
38
|
+
fsWait = null;
|
|
39
|
+
recompile();
|
|
40
|
+
}, 150); // Increased debounce for stability
|
|
41
|
+
}
|
|
42
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as AST from '../parser/AST.js';
|
|
2
|
+
/**
|
|
3
|
+
* LT → Lua Code Emitter
|
|
4
|
+
* Generates clean, readable Lua 5.4 code
|
|
5
|
+
*/
|
|
6
|
+
export declare class LuaEmitter {
|
|
7
|
+
private indent;
|
|
8
|
+
private output;
|
|
9
|
+
private tempId;
|
|
10
|
+
private usedVars;
|
|
11
|
+
private nextTemp;
|
|
12
|
+
emit(program: AST.Program): string;
|
|
13
|
+
private analyzeUsages;
|
|
14
|
+
private emitStatement;
|
|
15
|
+
private emitVariableDecl;
|
|
16
|
+
private hasSideEffects;
|
|
17
|
+
private emitArrayLiteral;
|
|
18
|
+
private emitAssignment;
|
|
19
|
+
private emitCompoundAssignment;
|
|
20
|
+
private emitIfStmt;
|
|
21
|
+
private emitForStmt;
|
|
22
|
+
private emitRangeForStmt;
|
|
23
|
+
private emitWhileStmt;
|
|
24
|
+
private emitReturnStmt;
|
|
25
|
+
private emitGuardStmt;
|
|
26
|
+
private emitSafeCall;
|
|
27
|
+
private emitThreadStmt;
|
|
28
|
+
private emitLoopStmt;
|
|
29
|
+
private emitWaitStmt;
|
|
30
|
+
private emitTimeoutStmt;
|
|
31
|
+
private emitIntervalStmt;
|
|
32
|
+
private emitTryCatch;
|
|
33
|
+
private emitEmitStmt;
|
|
34
|
+
private emitEventHandler;
|
|
35
|
+
private emitFunctionDecl;
|
|
36
|
+
private emitSwitchStmt;
|
|
37
|
+
private emitExportDecl;
|
|
38
|
+
private emitCommandStmt;
|
|
39
|
+
private emitExpr;
|
|
40
|
+
private emitInterpolatedString;
|
|
41
|
+
private emitBinaryExpr;
|
|
42
|
+
private emitUnaryExpr;
|
|
43
|
+
private emitCallExpr;
|
|
44
|
+
private emitMemberExpr;
|
|
45
|
+
private emitOptionalChain;
|
|
46
|
+
private emitNullCoalesce;
|
|
47
|
+
private emitArrowFunc;
|
|
48
|
+
private emitFunctionExpr;
|
|
49
|
+
private emitTableLiteral;
|
|
50
|
+
private emitVectorLiteral;
|
|
51
|
+
private emitUpdateExpr;
|
|
52
|
+
private emitConditionalExpr;
|
|
53
|
+
private emitStatementInline;
|
|
54
|
+
private hasContinue;
|
|
55
|
+
private emitBlock;
|
|
56
|
+
private emitBlockWithContinue;
|
|
57
|
+
private line;
|
|
58
|
+
private isSimpleExpr;
|
|
59
|
+
}
|