jlex 1.1.2 → 1.1.4
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 +86 -39
- package/examples/grammar.jison +3 -0
- package/examples/manual-lexer.js +87 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
|
|
2
|
+
# jlex [](https://www.npmjs.com/package/jlex)
|
|
3
|
+
|
|
1
4
|
## Install
|
|
2
5
|
|
|
3
6
|
`npm i jlex`
|
|
@@ -14,30 +17,30 @@ npx jlex <package name>
|
|
|
14
17
|
`jlex` is a tiny wrapper around `jison-lex` that allows you to use the script
|
|
15
18
|
`jlex` as standalone (`flex` like) processor.
|
|
16
19
|
|
|
17
|
-
Assuming the following lexer in file
|
|
20
|
+
Assuming the following lexer in file [examples/example.l](examples/example.l):
|
|
18
21
|
|
|
19
22
|
```
|
|
23
|
+
comment [/][*](.|[\r\n])*?[*][/]
|
|
20
24
|
%%
|
|
21
|
-
\s
|
|
25
|
+
\s+|{comment} /* skip whitespace */
|
|
22
26
|
[0-9]+ return 'NUMBER';
|
|
23
|
-
|
|
27
|
+
[-+*/] return 'OPERATOR';
|
|
24
28
|
<<EOF>> return 'EOF';
|
|
25
|
-
. return 'INVALID'
|
|
29
|
+
. return 'INVALID';
|
|
26
30
|
```
|
|
27
31
|
|
|
28
32
|
Compile it with:
|
|
29
33
|
|
|
30
34
|
```
|
|
31
|
-
|
|
32
|
-
Processing file: example
|
|
33
|
-
Writing file: example.js
|
|
35
|
+
npx jlex examples/example.l
|
|
34
36
|
```
|
|
35
37
|
|
|
36
|
-
This produces a Common.JS module `example.js` you can use with a simple `require` like in the file
|
|
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:
|
|
37
39
|
|
|
38
40
|
```js
|
|
39
41
|
const lex = require("./example");
|
|
40
|
-
|
|
42
|
+
const input = process.argv[2] || "2\n-/* a comment*/\n3";
|
|
43
|
+
lex.setInput(input);
|
|
41
44
|
|
|
42
45
|
const results = [];
|
|
43
46
|
|
|
@@ -46,38 +49,13 @@ results.push({ type: lex.lex(), lexeme: lex.yytext, loc: lex.yylloc });
|
|
|
46
49
|
results.push({ type: lex.lex(), lexeme: lex.yytext, loc: lex.yylloc });
|
|
47
50
|
results.push({ type: lex.lex(), lexeme: lex.yytext, loc: lex.yylloc });
|
|
48
51
|
|
|
49
|
-
console.log(results);
|
|
50
|
-
/*
|
|
51
|
-
➜ examples git:(main) ✗ node main.js
|
|
52
|
-
[
|
|
53
|
-
{
|
|
54
|
-
type: 'NUMBER',
|
|
55
|
-
lexeme: '2',
|
|
56
|
-
loc: { first_line: 1, last_line: 1, first_column: 0, last_column: 1 }
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
type: '-',
|
|
60
|
-
lexeme: '-',
|
|
61
|
-
loc: { first_line: 2, last_line: 2, first_column: 0, last_column: 1 }
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
type: 'NUMBER',
|
|
65
|
-
lexeme: '3',
|
|
66
|
-
loc: { first_line: 3, last_line: 3, first_column: 0, last_column: 1 }
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
type: 'EOF',
|
|
70
|
-
lexeme: '',
|
|
71
|
-
loc: { first_line: 3, last_line: 3, first_column: 1, last_column: 1 }
|
|
72
|
-
}
|
|
73
|
-
]
|
|
74
|
-
*/
|
|
52
|
+
console.log(results);
|
|
75
53
|
```
|
|
76
54
|
When you execute the former program, you get:
|
|
77
55
|
|
|
78
56
|
|
|
79
57
|
```js
|
|
80
|
-
➜ jlex
|
|
58
|
+
➜ jlex git:(main) ✗ node examples/main.js
|
|
81
59
|
[
|
|
82
60
|
{
|
|
83
61
|
type: 'NUMBER',
|
|
@@ -85,7 +63,7 @@ When you execute the former program, you get:
|
|
|
85
63
|
loc: { first_line: 1, last_line: 1, first_column: 0, last_column: 1 }
|
|
86
64
|
},
|
|
87
65
|
{
|
|
88
|
-
type: '
|
|
66
|
+
type: 'OPERATOR',
|
|
89
67
|
lexeme: '-',
|
|
90
68
|
loc: { first_line: 2, last_line: 2, first_column: 0, last_column: 1 }
|
|
91
69
|
},
|
|
@@ -104,7 +82,7 @@ When you execute the former program, you get:
|
|
|
104
82
|
|
|
105
83
|
## Using the lexer from a Jison grammar
|
|
106
84
|
|
|
107
|
-
In file [examples/grammar.jison](examples/grammar.jison) you'll find an example
|
|
85
|
+
In file [examples/grammar.jison](examples/grammar.jison#L32-L33) you'll find an example
|
|
108
86
|
of setting the generated lexer to be used from a Jison grammar. The key is
|
|
109
87
|
to set the `lex` attribute of the `parser` object to the generated lexer:
|
|
110
88
|
|
|
@@ -121,7 +99,7 @@ Compile the grammar with:
|
|
|
121
99
|
|
|
122
100
|
And use the parser:
|
|
123
101
|
|
|
124
|
-
```
|
|
102
|
+
```js
|
|
125
103
|
➜ jlex git:(main) ✗ node
|
|
126
104
|
Welcome to Node.js v25.6.0.
|
|
127
105
|
Type ".help" for more information.
|
|
@@ -183,4 +161,73 @@ Here is a description of the attributes of the lexer object:
|
|
|
183
161
|
rules: [ /^(?:\s+)/, /^(?:[0-9]+)/, /^(?:-)/, /^(?:$)/, /^(?:.)/ ],
|
|
184
162
|
conditions: { INITIAL: { rules: [Array], inclusive: true } }
|
|
185
163
|
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Writing a Jison compatible lexer by hand
|
|
167
|
+
|
|
168
|
+
See file [examples/manual-lexer.js](examples/manual-lexer.js) to see an example that
|
|
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
|
+
}
|
|
186
233
|
```
|
package/examples/grammar.jison
CHANGED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const lexer = {
|
|
2
|
+
input: "",
|
|
3
|
+
index: 0,
|
|
4
|
+
|
|
5
|
+
yytext: "",
|
|
6
|
+
yylineno: 0, // base 0 internamente
|
|
7
|
+
yylloc: {},
|
|
8
|
+
|
|
9
|
+
setInput(input) {
|
|
10
|
+
this.input = input;
|
|
11
|
+
this.index = 0;
|
|
12
|
+
this.yylineno = 0;
|
|
13
|
+
this.column = 0;
|
|
14
|
+
return this;
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
lex() {
|
|
18
|
+
if (this.index >= this.input.length) {
|
|
19
|
+
return 'EOF' /* this.EOF */;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Saltar espacios
|
|
23
|
+
while (/\s+/.test(this.input[this.index])) {
|
|
24
|
+
this.advance(this.input[this.index]);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (this.index >= this.input.length) {
|
|
28
|
+
return this.EOF;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const startLine = this.yylineno + 1; // Jison usa base 1
|
|
32
|
+
const startColumn = this.column;
|
|
33
|
+
|
|
34
|
+
const char = this.input[this.index];
|
|
35
|
+
|
|
36
|
+
// OPERATOR
|
|
37
|
+
if (/[-+*\/]/.test(char)) {
|
|
38
|
+
this.yytext = char;
|
|
39
|
+
this.advance(char);
|
|
40
|
+
|
|
41
|
+
this.yylloc = {
|
|
42
|
+
first_line: startLine,
|
|
43
|
+
last_line: this.yylineno + 1,
|
|
44
|
+
first_column: startColumn,
|
|
45
|
+
last_column: this.column
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return "OPERATOR";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// NUMBER (soporta múltiples dígitos)
|
|
52
|
+
if (/\d/.test(char)) {
|
|
53
|
+
let num = "";
|
|
54
|
+
|
|
55
|
+
while (/\d/.test(this.input[this.index])) {
|
|
56
|
+
num += this.input[this.index];
|
|
57
|
+
this.advance(this.input[this.index]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.yytext = num;
|
|
61
|
+
|
|
62
|
+
this.yylloc = {
|
|
63
|
+
first_line: startLine,
|
|
64
|
+
last_line: this.yylineno + 1,
|
|
65
|
+
first_column: startColumn,
|
|
66
|
+
last_column: this.column
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return "NUMBER";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
throw new Error("Carácter inesperado: " + char);
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
advance(char) {
|
|
76
|
+
this.index++;
|
|
77
|
+
|
|
78
|
+
if (char === "\n") {
|
|
79
|
+
this.yylineno++;
|
|
80
|
+
this.column = 0;
|
|
81
|
+
} else {
|
|
82
|
+
this.column++;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
module.exports = lexer;
|