jlex 1.1.3 → 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/README.md CHANGED
@@ -17,30 +17,30 @@ npx jlex <package name>
17
17
  `jlex` is a tiny wrapper around `jison-lex` that allows you to use the script
18
18
  `jlex` as standalone (`flex` like) processor.
19
19
 
20
- Assuming the following lexer in file `example.js`:
20
+ Assuming the following lexer in file [examples/example.l](examples/example.l):
21
21
 
22
22
  ```
23
+ comment [/][*](.|[\r\n])*?[*][/]
23
24
  %%
24
- \s+ /* skip whitespace */
25
+ \s+|{comment} /* skip whitespace */
25
26
  [0-9]+ return 'NUMBER';
26
- "-" return '-';
27
+ [-+*/] return 'OPERATOR';
27
28
  <<EOF>> return 'EOF';
28
- . return 'INVALID';%
29
+ . return 'INVALID';
29
30
  ```
30
31
 
31
32
  Compile it with:
32
33
 
33
34
  ```
34
- ➜ jlex-test npx jlex example
35
- Processing file: example
36
- Writing file: example.js
35
+ npx jlex examples/example.l
37
36
  ```
38
37
 
39
- This produces a Common.JS module `example.js` you can use with a simple `require` like in the file `main.js` below:
38
+ This produces a Common.JS module `examples/example.js` you can use with a simple `require` like in the file [main.js](examples/main.js) below:
40
39
 
41
40
  ```js
42
41
  const lex = require("./example");
43
- lex.setInput("2\n-\n3")
42
+ const input = process.argv[2] || "2\n-/* a comment*/\n3";
43
+ lex.setInput(input);
44
44
 
45
45
  const results = [];
46
46
 
@@ -49,38 +49,13 @@ results.push({ type: lex.lex(), lexeme: lex.yytext, loc: lex.yylloc });
49
49
  results.push({ type: lex.lex(), lexeme: lex.yytext, loc: lex.yylloc });
50
50
  results.push({ type: lex.lex(), lexeme: lex.yytext, loc: lex.yylloc });
51
51
 
52
- console.log(results);
53
- /*
54
- ➜ examples git:(main) ✗ node main.js
55
- [
56
- {
57
- type: 'NUMBER',
58
- lexeme: '2',
59
- loc: { first_line: 1, last_line: 1, first_column: 0, last_column: 1 }
60
- },
61
- {
62
- type: '-',
63
- lexeme: '-',
64
- loc: { first_line: 2, last_line: 2, first_column: 0, last_column: 1 }
65
- },
66
- {
67
- type: 'NUMBER',
68
- lexeme: '3',
69
- loc: { first_line: 3, last_line: 3, first_column: 0, last_column: 1 }
70
- },
71
- {
72
- type: 'EOF',
73
- lexeme: '',
74
- loc: { first_line: 3, last_line: 3, first_column: 1, last_column: 1 }
75
- }
76
- ]
77
- */
52
+ console.log(results);
78
53
  ```
79
54
  When you execute the former program, you get:
80
55
 
81
56
 
82
57
  ```js
83
- ➜ jlex-test node main.js
58
+ ➜ jlex git:(main) ✗ node examples/main.js
84
59
  [
85
60
  {
86
61
  type: 'NUMBER',
@@ -88,7 +63,7 @@ When you execute the former program, you get:
88
63
  loc: { first_line: 1, last_line: 1, first_column: 0, last_column: 1 }
89
64
  },
90
65
  {
91
- type: '-',
66
+ type: 'OPERATOR',
92
67
  lexeme: '-',
93
68
  loc: { first_line: 2, last_line: 2, first_column: 0, last_column: 1 }
94
69
  },
@@ -124,7 +99,7 @@ Compile the grammar with:
124
99
 
125
100
  And use the parser:
126
101
 
127
- ```
102
+ ```js
128
103
  ➜ jlex git:(main) ✗ node
129
104
  Welcome to Node.js v25.6.0.
130
105
  Type ".help" for more information.
@@ -191,4 +166,68 @@ Here is a description of the attributes of the lexer object:
191
166
  ## Writing a Jison compatible lexer by hand
192
167
 
193
168
  See file [examples/manual-lexer.js](examples/manual-lexer.js) to see an example that
194
- illustrates how to write a Jison compatible lexer by hand.
169
+ illustrates how to write a Jison compatible lexer by hand.
170
+
171
+ To use with the [grammar](examples/grammar.jison#L33-L34) in the `examples` folder, set the `parser.lexer` to the hand-written one:
172
+
173
+ ```diff
174
+ ➜ jlex git:(main) ✗ git -P diff examples/grammar.jison
175
+ diff --git a/examples/grammar.jison b/examples/grammar.jison
176
+ index 0b99d40..c9e974d 100644
177
+ --- a/examples/grammar.jison
178
+ +++ b/examples/grammar.jison
179
+ @@ -12,6 +12,7 @@ expr
180
+ {
181
+ $$ = {
182
+ type: "OPERATOR",
183
+ + lexeme: $2,
184
+ left: $1,
185
+ right: $3,
186
+ loc: @2
187
+ @@ -29,5 +30,7 @@ expr
188
+
189
+ %%
190
+
191
+ -const lexer = require("./example.js");
192
+ +//const lexer = require("./example.js");
193
+ +const lexer = require("./manual-lexer.js");
194
+ +
195
+ parser.lexer = lexer;
196
+
197
+ ```
198
+
199
+ Then compile the grammar:
200
+
201
+ ```
202
+ ➜ jlex git:(main) ✗ npx jison examples/grammar.jison -o examples/parser.js
203
+ ```
204
+ and use it like this:
205
+
206
+ ```js
207
+ ➜ jlex git:(main) ✗ node
208
+ Welcome to Node.js v25.6.0.
209
+ Type ".help" for more information.
210
+ > p = require("./examples/parser.js")
211
+ {
212
+ parser: { yy: {} },
213
+ Parser: [Function: Parser],
214
+ parse: [Function (anonymous)],
215
+ main: [Function: commonjsMain]
216
+ }
217
+ > p.parse("2*3")
218
+ {
219
+ type: 'OPERATOR',
220
+ lexeme: '*',
221
+ left: {
222
+ type: 'number',
223
+ value: 2,
224
+ loc: { first_line: 1, last_line: 1, first_column: 0, last_column: 1 }
225
+ },
226
+ right: {
227
+ type: 'number',
228
+ value: 3,
229
+ loc: { first_line: 1, last_line: 1, first_column: 2, last_column: 3 }
230
+ },
231
+ loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 2 }
232
+ }
233
+ ```
@@ -12,6 +12,7 @@ expr
12
12
  {
13
13
  $$ = {
14
14
  type: "OPERATOR",
15
+ lexeme: $2,
15
16
  left: $1,
16
17
  right: $3,
17
18
  loc: @2
@@ -30,4 +31,6 @@ expr
30
31
  %%
31
32
 
32
33
  const lexer = require("./example.js");
34
+ //const lexer = require("./manual-lexer.js");
35
+
33
36
  parser.lexer = lexer;
@@ -16,7 +16,7 @@ const lexer = {
16
16
 
17
17
  lex() {
18
18
  if (this.index >= this.input.length) {
19
- return this.EOF;
19
+ return 'EOF' /* this.EOF */;
20
20
  }
21
21
 
22
22
  // Saltar espacios
@@ -33,9 +33,9 @@ const lexer = {
33
33
 
34
34
  const char = this.input[this.index];
35
35
 
36
- // PLUS
37
- if (char === "+") {
38
- this.yytext = "+";
36
+ // OPERATOR
37
+ if (/[-+*\/]/.test(char)) {
38
+ this.yytext = char;
39
39
  this.advance(char);
40
40
 
41
41
  this.yylloc = {
@@ -45,7 +45,7 @@ const lexer = {
45
45
  last_column: this.column
46
46
  };
47
47
 
48
- return "PLUS";
48
+ return "OPERATOR";
49
49
  }
50
50
 
51
51
  // NUMBER (soporta múltiples dígitos)
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 { execSync } = require('child_process');
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
- .version(packageJson.version)
12
- .description('A tiny wrapper around jison-lex that allows you to use jison-lex as a standalone (flex like) processor.')
13
- .addHelpText('after', `See https://github.com/ULL-ESIT-PL/jlex/blob/main/README.md for more help`)
14
- .option("-o <fileName>", "Output file name")
15
- .usage("[options] <filename>");
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
- const {dir,name } = path.parse(fileName); // { dir, base, ext, name }
23
- const outputFileName = options.o || path.join(dir, `${name}.js`);
24
- const outputParse = path.parse(outputFileName);
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
- execSync(shellCommand, { encoding: 'utf-8' });
29
- let lexerStr = fs.readFileSync(outputFileName, "utf8").toString();
30
- let lexerModule = lexerStr.replace(new RegExp(`var ${outputParse.name} =`), `\nmodule.exports =`);
31
- console.log("Writing file:", outputFileName);
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
- console.error("Error:", error.message);
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jlex",
3
- "version": "1.1.3",
3
+ "version": "1.2.0",
4
4
  "description": "A wrapper around jison-lex to make it work as a standalone program like Flex",
5
5
  "keywords": [
6
6
  "jison-lex",