@@ -70,6 +70,88 @@ 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 ;
87
+ this . shouldFail = false ;
88
+ const wasWithinStrLiteral = this . _literal !== null ;
89
+
90
+ for ( const current of line ) {
91
+ if ( previous === '\\' ) {
92
+ // valid escaping, skip processing. previous doesn't matter anymore
93
+ previous = null ;
94
+ continue ;
95
+ }
96
+
97
+ if ( ! this . _literal ) {
98
+ if ( previous === '*' && current === '/' ) {
99
+ if ( this . blockComment ) {
100
+ this . blockComment = false ;
101
+ previous = null ;
102
+ continue ;
103
+ } else {
104
+ this . shouldFail = true ;
105
+ break ;
106
+ }
107
+ }
108
+
109
+ // ignore rest of the line if `current` and `previous` are `/`s
110
+ if ( previous === current && previous === '/' && ! this . blockComment ) {
111
+ break ;
112
+ }
113
+
114
+ if ( previous === '/' && current === '*' ) {
115
+ this . blockComment = true ;
116
+ previous = null ;
117
+ }
118
+ }
119
+
120
+ if ( this . blockComment ) continue ;
121
+
122
+ if ( current === this . _literal ) {
123
+ this . _literal = null ;
124
+ } else if ( current === '\'' || current === '"' ) {
125
+ this . _literal = this . _literal || current ;
126
+ }
127
+
128
+ previous = current ;
129
+ }
130
+
131
+ const isWithinStrLiteral = this . _literal !== null ;
132
+
133
+ if ( ! wasWithinStrLiteral && ! isWithinStrLiteral ) {
134
+ // Current line has nothing to do with String literals, trim both ends
135
+ line = line . trim ( ) ;
136
+ } else if ( wasWithinStrLiteral && ! isWithinStrLiteral ) {
137
+ // was part of a string literal, but it is over now, trim only the end
138
+ line = line . trimRight ( ) ;
139
+ } else if ( isWithinStrLiteral && ! wasWithinStrLiteral ) {
140
+ // was not part of a string literal, but it is now, trim only the start
141
+ line = line . trimLeft ( ) ;
142
+ }
143
+
144
+ const lastChar = line . charAt ( line . length - 1 ) ;
145
+
146
+ this . shouldFail = this . shouldFail ||
147
+ ( ( ! this . _literal && lastChar === '\\' ) ||
148
+ ( this . _literal && lastChar !== '\\' ) ) ;
149
+
150
+ return line ;
151
+ }
152
+ }
153
+
154
+
73
155
function REPLServer ( prompt ,
74
156
stream ,
75
157
eval_ ,
@@ -193,7 +275,7 @@ function REPLServer(prompt,
193
275
debug ( 'domain error' ) ;
194
276
const top = replMap . get ( self ) ;
195
277
top . outputStream . write ( ( e . stack || e ) + '\n' ) ;
196
- top . _currentStringLiteral = null ;
278
+ top . lineParser . reset ( ) ;
197
279
top . bufferedCommand = '' ;
198
280
top . lines . level = [ ] ;
199
281
top . displayPrompt ( ) ;
@@ -220,8 +302,7 @@ function REPLServer(prompt,
220
302
self . outputStream = output ;
221
303
222
304
self . resetContext ( ) ;
223
- // Initialize the current string literal found, to be null
224
- self . _currentStringLiteral = null ;
305
+ self . lineParser = new LineParser ( ) ;
225
306
self . bufferedCommand = '' ;
226
307
self . lines . level = [ ] ;
227
308
@@ -280,87 +361,22 @@ function REPLServer(prompt,
280
361
sawSIGINT = false ;
281
362
}
282
363
283
- self . _currentStringLiteral = null ;
364
+ self . lineParser . reset ( ) ;
284
365
self . bufferedCommand = '' ;
285
366
self . lines . level = [ ] ;
286
367
self . displayPrompt ( ) ;
287
368
} ) ;
288
369
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
370
self . on ( 'line' , function ( cmd ) {
339
371
debug ( 'line %j' , cmd ) ;
340
372
sawSIGINT = false ;
341
373
var skipCatchall = false ;
342
- var finisherFn = finish ;
343
374
344
375
// leading whitespaces in template literals should not be trimmed.
345
376
if ( self . _inTemplateLiteral ) {
346
377
self . _inTemplateLiteral = false ;
347
378
} 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 ) ;
379
+ cmd = self . lineParser . parseLine ( cmd ) ;
364
380
}
365
381
366
382
// Check to see if a REPL keyword was used. If it returns true,
@@ -393,9 +409,9 @@ function REPLServer(prompt,
393
409
}
394
410
395
411
debug ( 'eval %j' , evalCmd ) ;
396
- self . eval ( evalCmd , self . context , 'repl' , finisherFn ) ;
412
+ self . eval ( evalCmd , self . context , 'repl' , finish ) ;
397
413
} else {
398
- finisherFn ( null ) ;
414
+ finish ( null ) ;
399
415
}
400
416
401
417
function finish ( e , ret ) {
@@ -406,15 +422,15 @@ function REPLServer(prompt,
406
422
self . outputStream . write ( 'npm should be run outside of the ' +
407
423
'node repl, in your normal shell.\n' +
408
424
'(Press Control-D to exit.)\n' ) ;
409
- self . _currentStringLiteral = null ;
425
+ self . lineParser . reset ( ) ;
410
426
self . bufferedCommand = '' ;
411
427
self . displayPrompt ( ) ;
412
428
return ;
413
429
}
414
430
415
431
// If error was SyntaxError and not JSON.parse error
416
432
if ( e ) {
417
- if ( e instanceof Recoverable ) {
433
+ if ( e instanceof Recoverable && ! self . lineParser . shouldFail ) {
418
434
// Start buffering data like that:
419
435
// {
420
436
// ... x: 1
@@ -423,12 +439,12 @@ function REPLServer(prompt,
423
439
self . displayPrompt ( ) ;
424
440
return ;
425
441
} else {
426
- self . _domain . emit ( 'error' , e ) ;
442
+ self . _domain . emit ( 'error' , e . err || e ) ;
427
443
}
428
444
}
429
445
430
446
// Clear buffer if no SyntaxErrors
431
- self . _currentStringLiteral = null ;
447
+ self . lineParser . reset ( ) ;
432
448
self . bufferedCommand = '' ;
433
449
434
450
// If we got any output - print it (if no error)
@@ -985,7 +1001,7 @@ function defineDefaultCommands(repl) {
985
1001
repl . defineCommand ( 'break' , {
986
1002
help : 'Sometimes you get stuck, this gets you out' ,
987
1003
action : function ( ) {
988
- this . _currentStringLiteral = null ;
1004
+ this . lineParser . reset ( ) ;
989
1005
this . bufferedCommand = '' ;
990
1006
this . displayPrompt ( ) ;
991
1007
}
@@ -1000,7 +1016,7 @@ function defineDefaultCommands(repl) {
1000
1016
repl . defineCommand ( 'clear' , {
1001
1017
help : clearMessage ,
1002
1018
action : function ( ) {
1003
- this . _currentStringLiteral = null ;
1019
+ this . lineParser . reset ( ) ;
1004
1020
this . bufferedCommand = '' ;
1005
1021
if ( ! this . useGlobal ) {
1006
1022
this . outputStream . write ( 'Clearing context...\n' ) ;
0 commit comments