@@ -150,6 +150,9 @@ export class Tokenizer {
150
150
151
151
private advance ( ) {
152
152
const res = this . source [ this . current ] ;
153
+ if ( this . peek ( ) == '\n' ) {
154
+ this . line += 1 ;
155
+ }
153
156
this . current += 1 ;
154
157
this . col += 1 ;
155
158
return res ;
@@ -178,10 +181,19 @@ export class Tokenizer {
178
181
private addStringToken ( type : TokenType ) {
179
182
const line = this . line
180
183
const col = this . col ;
184
+ // Remove starting and ending quotes when slicing
185
+ // Ensures that string is parsed properly
181
186
const lexeme = this . source . slice ( this . start + 1 , this . current - 1 ) ;
182
187
this . tokens . push ( new Token ( type , lexeme , line , col , this . current - lexeme . length ) )
183
188
}
184
189
190
+ private addMultiLineStringToken ( type : TokenType ) {
191
+ const line = this . line
192
+ const col = this . col ;
193
+ // Remove three starting and ending quotes when slicing
194
+ const lexeme = this . source . slice ( this . start + 3 , this . current - 3 ) ;
195
+ this . tokens . push ( new Token ( type , lexeme , line , col , this . current - lexeme . length ) )
196
+ }
185
197
// Checks that the current character matches a pattern. If so the character is consumed, else nothing is consumed.
186
198
private matches ( pattern : string ) : boolean {
187
199
if ( this . isAtEnd ( ) ) {
@@ -432,26 +444,45 @@ export class Tokenizer {
432
444
break ;
433
445
// String
434
446
case '"' :
435
- while ( this . peek ( ) != '"' && this . peek ( ) != '\n' && ! this . isAtEnd ( ) ) {
436
- this . advance ( ) ;
437
- }
438
- if ( this . peek ( ) === '\n' || this . isAtEnd ( ) ) {
439
- throw new TokenizerErrors . UnterminatedStringError ( this . line , this . col , this . source , this . start , this . current ) ;
440
- }
441
- // Consume closing "
442
- this . advance ( ) ;
443
- this . addStringToken ( TokenType . STRING ) ;
444
- break ;
445
- case '\'' :
446
- while ( this . peek ( ) != '\'' && this . peek ( ) != '\n' && ! this . isAtEnd ( ) ) {
447
+ case "'" :
448
+ let quote = c ;
449
+ if ( this . peek ( ) == quote ) { // handle multi-line string
450
+ this . advance ( ) ; // second quote found and consumed
451
+ if ( this . peek ( ) != quote ) { // empty string ""
452
+ this . addStringToken ( TokenType . STRING ) ;
453
+ break ;
454
+ }
455
+ this . advance ( ) ; // third quote consumed
456
+ while ( this . peek ( ) != quote && ! this . isAtEnd ( ) ) {
457
+ this . advance ( ) ; // advance until ending quote found
458
+ }
459
+ if ( this . isAtEnd ( ) ) {
460
+ throw new TokenizerErrors . UnterminatedStringError ( this . line ,
461
+ this . col , this . source , this . start , this . current ) ;
462
+ }
463
+ this . advance ( ) ; // consume first ending quote
464
+ if ( this . peek ( ) != quote ) {
465
+ throw new TokenizerErrors . UnterminatedStringError ( this . line ,
466
+ this . col , this . source , this . start , this . current ) ;
467
+ }
468
+ this . advance ( ) ; // consume second ending quote
469
+ if ( this . peek ( ) != quote ) {
470
+ throw new TokenizerErrors . UnterminatedStringError ( this . line ,
471
+ this . col , this . source , this . start , this . current ) ;
472
+ }
473
+ this . advance ( ) ; // consume third ending quote
474
+ this . addMultiLineStringToken ( TokenType . STRING ) ;
475
+ } else { // other case, single-line string
476
+ while ( this . peek ( ) != quote && this . peek ( ) != '\n' && ! this . isAtEnd ( ) ) {
477
+ this . advance ( ) ;
478
+ }
479
+ if ( this . peek ( ) === '\n' || this . isAtEnd ( ) ) {
480
+ throw new TokenizerErrors . UnterminatedStringError ( this . line , this . col , this . source , this . start , this . current ) ;
481
+ }
482
+ // Consume Closing "
447
483
this . advance ( ) ;
484
+ this . addStringToken ( TokenType . STRING ) ;
448
485
}
449
- if ( this . peek ( ) === '\n' || this . isAtEnd ( ) ) {
450
- throw new TokenizerErrors . UnterminatedStringError ( this . line , this . col , this . source , this . start , this . current ) ;
451
- }
452
- // Consume closing '
453
- this . advance ( ) ;
454
- this . addStringToken ( TokenType . STRING ) ;
455
486
break ;
456
487
// Number... I wish JS had match statements :(
457
488
case '0' :
0 commit comments