@@ -70,6 +70,90 @@ const BLOCK_SCOPED_ERROR = 'Block-scoped declarations (let, ' +
70
70
'const, function, class) not yet supported outside strict mode' ;
71
71
72
72
73
+ class LineParser {
74
+
75
+ constructor ( ) {
76
+ this . reset ( ) ;
77
+ }
78
+
79
+ reset ( ) {
80
+ this . _literal = null ;
81
+ this . shouldFail = false ;
82
+ this . blockComment = false ;
83
+ }
84
+
85
+ parseLine ( line ) {
86
+ var previous = null , current = null ;
87
+ this . shouldFail = false ;
88
+ const wasWithinStrLiteral = this . _literal !== null ;
89
+
90
+ for ( let i = 0 ; i < line . length ; i += 1 ) {
91
+ if ( previous === '\\' ) {
92
+ // valid escaping, skip processing. previous doesn't matter anymore
93
+ previous = null ;
94
+ continue ;
95
+ }
96
+
97
+ current = line . charAt ( i ) ;
98
+
99
+ if ( ! this . _literal ) {
100
+ if ( previous === '*' && current === '/' ) {
101
+ if ( this . blockComment ) {
102
+ this . blockComment = false ;
103
+ previous = null ;
104
+ continue ;
105
+ } else {
106
+ this . shouldFail = true ;
107
+ break ;
108
+ }
109
+ }
110
+
111
+ // ignore rest of the line if `current` and `previous` are `/`s
112
+ if ( previous === current && previous === '/' && ! this . blockComment ) {
113
+ break ;
114
+ }
115
+
116
+ if ( previous === '/' && current === '*' ) {
117
+ this . blockComment = true ;
118
+ previous = null ;
119
+ }
120
+ }
121
+
122
+ if ( this . blockComment ) continue ;
123
+
124
+ if ( current === this . _literal ) {
125
+ this . _literal = null ;
126
+ } else if ( current === '\'' || current === '"' ) {
127
+ this . _literal = this . _literal || current ;
128
+ }
129
+
130
+ previous = current ;
131
+ }
132
+
133
+ const isWithinStrLiteral = this . _literal !== null ;
134
+
135
+ if ( ! wasWithinStrLiteral && ! isWithinStrLiteral ) {
136
+ // Current line has nothing to do with String literals, trim both ends
137
+ line = line . trim ( ) ;
138
+ } else if ( wasWithinStrLiteral && ! isWithinStrLiteral ) {
139
+ // was part of a string literal, but it is over now, trim only the end
140
+ line = line . trimRight ( ) ;
141
+ } else if ( isWithinStrLiteral && ! wasWithinStrLiteral ) {
142
+ // was not part of a string literal, but it is now, trim only the start
143
+ line = line . trimLeft ( ) ;
144
+ }
145
+
146
+ const lastChar = line . charAt ( line . length - 1 ) ;
147
+
148
+ this . shouldFail = this . shouldFail ||
149
+ ( ( ! this . _literal && lastChar === '\\' ) ||
150
+ ( this . _literal && lastChar !== '\\' ) ) ;
151
+
152
+ return line ;
153
+ }
154
+ }
155
+
156
+
73
157
function REPLServer ( prompt ,
74
158
stream ,
75
159
eval_ ,
@@ -193,7 +277,7 @@ function REPLServer(prompt,
193
277
debug ( 'domain error' ) ;
194
278
const top = replMap . get ( self ) ;
195
279
top . outputStream . write ( ( e . stack || e ) + '\n' ) ;
196
- top . _currentStringLiteral = null ;
280
+ top . lineParser . reset ( ) ;
197
281
top . bufferedCommand = '' ;
198
282
top . lines . level = [ ] ;
199
283
top . displayPrompt ( ) ;
@@ -221,7 +305,7 @@ function REPLServer(prompt,
221
305
222
306
self . resetContext ( ) ;
223
307
// Initialize the current string literal found, to be null
224
- self . _currentStringLiteral = null ;
308
+ self . lineParser = new LineParser ( ) ;
225
309
self . bufferedCommand = '' ;
226
310
self . lines . level = [ ] ;
227
311
@@ -280,87 +364,22 @@ function REPLServer(prompt,
280
364
sawSIGINT = false ;
281
365
}
282
366
283
- self . _currentStringLiteral = null ;
367
+ self . lineParser . reset ( ) ;
284
368
self . bufferedCommand = '' ;
285
369
self . lines . level = [ ] ;
286
370
self . displayPrompt ( ) ;
287
371
} ) ;
288
372
289
- function parseLine ( line , currentStringLiteral ) {
290
- var previous = null , current = null ;
291
-
292
- for ( var i = 0 ; i < line . length ; i += 1 ) {
293
- if ( previous === '\\' ) {
294
- // if it is a valid escaping, then skip processing and the previous
295
- // character doesn't matter anymore.
296
- previous = null ;
297
- continue ;
298
- }
299
-
300
- current = line . charAt ( i ) ;
301
- if ( current === currentStringLiteral ) {
302
- currentStringLiteral = null ;
303
- } else if ( current === '\'' ||
304
- current === '"' &&
305
- currentStringLiteral === null ) {
306
- currentStringLiteral = current ;
307
- }
308
- previous = current ;
309
- }
310
-
311
- return currentStringLiteral ;
312
- }
313
-
314
- function getFinisherFunction ( cmd , defaultFn ) {
315
- if ( ( self . _currentStringLiteral === null &&
316
- cmd . charAt ( cmd . length - 1 ) === '\\' ) ||
317
- ( self . _currentStringLiteral !== null &&
318
- cmd . charAt ( cmd . length - 1 ) !== '\\' ) ) {
319
-
320
- // If the line continuation is used outside string literal or if the
321
- // string continuation happens with out line continuation, then fail hard.
322
- // Even if the error is recoverable, get the underlying error and use it.
323
- return function ( e , ret ) {
324
- var error = e instanceof Recoverable ? e . err : e ;
325
-
326
- if ( arguments . length === 2 ) {
327
- // using second argument only if it is actually passed. Otherwise
328
- // `undefined` will be printed when invalid REPL commands are used.
329
- return defaultFn ( error , ret ) ;
330
- }
331
-
332
- return defaultFn ( error ) ;
333
- } ;
334
- }
335
- return defaultFn ;
336
- }
337
-
338
373
self . on ( 'line' , function ( cmd ) {
339
374
debug ( 'line %j' , cmd ) ;
340
375
sawSIGINT = false ;
341
376
var skipCatchall = false ;
342
- var finisherFn = finish ;
343
377
344
378
// leading whitespaces in template literals should not be trimmed.
345
379
if ( self . _inTemplateLiteral ) {
346
380
self . _inTemplateLiteral = false ;
347
381
} else {
348
- const wasWithinStrLiteral = self . _currentStringLiteral !== null ;
349
- self . _currentStringLiteral = parseLine ( cmd , self . _currentStringLiteral ) ;
350
- const isWithinStrLiteral = self . _currentStringLiteral !== null ;
351
-
352
- if ( ! wasWithinStrLiteral && ! isWithinStrLiteral ) {
353
- // Current line has nothing to do with String literals, trim both ends
354
- cmd = cmd . trim ( ) ;
355
- } else if ( wasWithinStrLiteral && ! isWithinStrLiteral ) {
356
- // was part of a string literal, but it is over now, trim only the end
357
- cmd = cmd . trimRight ( ) ;
358
- } else if ( isWithinStrLiteral && ! wasWithinStrLiteral ) {
359
- // was not part of a string literal, but it is now, trim only the start
360
- cmd = cmd . trimLeft ( ) ;
361
- }
362
-
363
- finisherFn = getFinisherFunction ( cmd , finish ) ;
382
+ cmd = self . lineParser . parseLine ( cmd ) ;
364
383
}
365
384
366
385
// Check to see if a REPL keyword was used. If it returns true,
@@ -393,9 +412,9 @@ function REPLServer(prompt,
393
412
}
394
413
395
414
debug ( 'eval %j' , evalCmd ) ;
396
- self . eval ( evalCmd , self . context , 'repl' , finisherFn ) ;
415
+ self . eval ( evalCmd , self . context , 'repl' , finish ) ;
397
416
} else {
398
- finisherFn ( null ) ;
417
+ finish ( null ) ;
399
418
}
400
419
401
420
function finish ( e , ret ) {
@@ -406,15 +425,15 @@ function REPLServer(prompt,
406
425
self . outputStream . write ( 'npm should be run outside of the ' +
407
426
'node repl, in your normal shell.\n' +
408
427
'(Press Control-D to exit.)\n' ) ;
409
- self . _currentStringLiteral = null ;
428
+ self . lineParser . reset ( ) ;
410
429
self . bufferedCommand = '' ;
411
430
self . displayPrompt ( ) ;
412
431
return ;
413
432
}
414
433
415
434
// If error was SyntaxError and not JSON.parse error
416
435
if ( e ) {
417
- if ( e instanceof Recoverable ) {
436
+ if ( e instanceof Recoverable && ! self . lineParser . shouldFail ) {
418
437
// Start buffering data like that:
419
438
// {
420
439
// ... x: 1
@@ -423,12 +442,12 @@ function REPLServer(prompt,
423
442
self . displayPrompt ( ) ;
424
443
return ;
425
444
} else {
426
- self . _domain . emit ( 'error' , e ) ;
445
+ self . _domain . emit ( 'error' , e . err || e ) ;
427
446
}
428
447
}
429
448
430
449
// Clear buffer if no SyntaxErrors
431
- self . _currentStringLiteral = null ;
450
+ self . lineParser . reset ( ) ;
432
451
self . bufferedCommand = '' ;
433
452
434
453
// If we got any output - print it (if no error)
@@ -985,7 +1004,7 @@ function defineDefaultCommands(repl) {
985
1004
repl . defineCommand ( 'break' , {
986
1005
help : 'Sometimes you get stuck, this gets you out' ,
987
1006
action : function ( ) {
988
- this . _currentStringLiteral = null ;
1007
+ this . lineParser . reset ( ) ;
989
1008
this . bufferedCommand = '' ;
990
1009
this . displayPrompt ( ) ;
991
1010
}
@@ -1000,7 +1019,7 @@ function defineDefaultCommands(repl) {
1000
1019
repl . defineCommand ( 'clear' , {
1001
1020
help : clearMessage ,
1002
1021
action : function ( ) {
1003
- this . _currentStringLiteral = null ;
1022
+ this . lineParser . reset ( ) ;
1004
1023
this . bufferedCommand = '' ;
1005
1024
if ( ! this . useGlobal ) {
1006
1025
this . outputStream . write ( 'Clearing context...\n' ) ;
0 commit comments