jlex 1.1.4 → 1.2.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/jlex.js +113 -15
- package/package.json +1 -1
package/jlex.js
CHANGED
|
@@ -1,35 +1,133 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// replace example by the name of the generated module
|
|
3
3
|
const fs = require("fs");
|
|
4
|
-
const
|
|
4
|
+
const jisonLex = require('jison-lex');
|
|
5
|
+
|
|
5
6
|
const { Command } = require('commander')
|
|
6
7
|
const packageJson = require('./package.json')
|
|
7
8
|
const path = require('path');
|
|
8
9
|
const program = new Command();
|
|
9
10
|
|
|
10
11
|
program
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
.version(packageJson.version)
|
|
13
|
+
.description('A tiny wrapper around jison-lex that allows you to use jison-lex as a standalone (flex like) processor.')
|
|
14
|
+
.addHelpText('after', `See https://github.com/ULL-ESIT-PL/jlex/blob/main/README.md for more help`)
|
|
15
|
+
.option("-o, --output <fileName>", "Output file name")
|
|
16
|
+
.option("-v, --verbose", "Enable verbose output")
|
|
17
|
+
.usage("[options] <filename>");
|
|
16
18
|
|
|
17
19
|
program.parse(process.argv);
|
|
18
20
|
const options = program.opts();
|
|
19
21
|
|
|
22
|
+
// Logging helper
|
|
23
|
+
function log(message, isVerbose = false) {
|
|
24
|
+
if (!isVerbose || options.verbose) {
|
|
25
|
+
console.log(message);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function logError(message) {
|
|
30
|
+
console.error(`❌ Error: ${message}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function logSuccess(message) {
|
|
34
|
+
console.log(`✅ ${message}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (program.args.length === 0) {
|
|
38
|
+
logError("No input file specified");
|
|
39
|
+
program.help();
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
20
42
|
const fileName = program.args[0];
|
|
21
43
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
44
|
+
// Validate input file extension
|
|
45
|
+
if (!/[.](l|lex|flex)$/.test(fileName)) {
|
|
46
|
+
console.warn(`⚠️ Warning: Expected .l or .lex extension for lexer file, got: ${path.extname(fileName)}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const { dir, name } = path.parse(fileName);
|
|
50
|
+
const outputFileName = options.output || path.join(dir, `${name}.js`);
|
|
25
51
|
|
|
26
|
-
const shellCommand = `npx jison-lex ${fileName} -o ${outputFileName}`;
|
|
27
52
|
try {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
53
|
+
if (!fs.existsSync(fileName)) {
|
|
54
|
+
logError(`File '${fileName}' does not exist`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const stats = fs.statSync(fileName);
|
|
59
|
+
if (!stats.isFile()) {
|
|
60
|
+
logError(`'${fileName}' is not a regular file`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
log(`📖 Reading lexer grammar from: ${fileName}`, true);
|
|
65
|
+
let lexerStr = fs.readFileSync(fileName, "utf8");
|
|
66
|
+
|
|
67
|
+
let generatedCode = jisonLex.generate(lexerStr, { moduleType: 'commonjs' });
|
|
68
|
+
|
|
69
|
+
if (!generatedCode || generatedCode.trim().length === 0) {
|
|
70
|
+
logError(`jison-lex failed to generate code from '${fileName}'`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
log(`📝 Generated ${generatedCode.length} characters of lexer code`, true);
|
|
75
|
+
|
|
76
|
+
// More robust transformation with multiple patterns
|
|
77
|
+
const patterns = [
|
|
78
|
+
/var\s+lexer\s*=/,
|
|
79
|
+
/let\s+lexer\s*=/,
|
|
80
|
+
/const\s+lexer\s*=/,
|
|
81
|
+
new RegExp(`var\\s+${name}\\s*=`)
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
let lexerModule = generatedCode;
|
|
85
|
+
let transformApplied = false;
|
|
86
|
+
|
|
87
|
+
for (const pattern of patterns) {
|
|
88
|
+
if (pattern.test(generatedCode)) {
|
|
89
|
+
lexerModule = generatedCode.replace(pattern, `module.exports =`);
|
|
90
|
+
transformApplied = true;
|
|
91
|
+
log(`🔄 Applied transformation pattern: ${pattern}`, true);
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
//transformApplied = false;
|
|
97
|
+
if (!transformApplied) {
|
|
98
|
+
logError(
|
|
99
|
+
`No standard pattern found in the generated code!.
|
|
100
|
+
Applied patterns: ${patterns.map(p => p.toString()).join(' or ')}.
|
|
101
|
+
Output may not be a valid CommonJS module.
|
|
102
|
+
Consider adding an issue: https://github.com/ULL-ESIT-PL/jlex/issues.
|
|
103
|
+
`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Ensure output directory exists
|
|
107
|
+
const outputDir = path.dirname(outputFileName);
|
|
108
|
+
if (!fs.existsSync(outputDir)) {
|
|
109
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
110
|
+
log(`📁 Created directory: ${outputDir}`, true);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
log(`📝 Writing file: ${outputFileName}`);
|
|
32
114
|
fs.writeFileSync(outputFileName, lexerModule);
|
|
115
|
+
|
|
116
|
+
logSuccess(`Successfully processed ${fileName} → ${outputFileName}`);
|
|
117
|
+
log(`📊 Output size: ${(fs.statSync(outputFileName).size / 1024).toFixed(1)}KB`, true);
|
|
118
|
+
|
|
33
119
|
} catch (error) {
|
|
34
|
-
|
|
120
|
+
if (error.message.includes('Lexical error') || error.message.includes('Parse error')) {
|
|
121
|
+
logError(`Invalid lexer grammar in '${fileName}': ${error.message}`);
|
|
122
|
+
} else if (error.code === 'EACCES') {
|
|
123
|
+
logError(`Permission denied accessing '${error.path}'`);
|
|
124
|
+
} else if (error.code === 'ENOSPC') {
|
|
125
|
+
logError(`No space left on device when writing '${outputFileName}'`);
|
|
126
|
+
} else {
|
|
127
|
+
logError(error.message);
|
|
128
|
+
if (options.verbose) {
|
|
129
|
+
console.error('Stack trace:', error.stack);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
process.exit(1);
|
|
35
133
|
}
|