@@ -206,38 +206,29 @@ class Parser {
206206 this . skipWhitespace ( ) ;
207207 const ch = this . peek ( ) ;
208208
209+ // Single-character unary operators
209210 if ( ch === "!" || ch === "~" || ch === "+" || ch === "-" ) {
210211 this . advance ( ) ;
211212 this . skipWhitespace ( ) ;
212- const argument = this . parseUnary ( ) ;
213213 return {
214214 type : "UnaryExpr" ,
215215 operator : ch ,
216- argument,
216+ argument : this . parseUnary ( ) ,
217217 prefix : true ,
218218 } ;
219219 }
220220
221- if ( this . matchKeyword ( "typeof" ) ) {
222- this . skipWhitespace ( ) ;
223- const argument = this . parseUnary ( ) ;
224- return {
225- type : "UnaryExpr" ,
226- operator : "typeof" ,
227- argument,
228- prefix : true ,
229- } ;
230- }
231-
232- if ( this . matchKeyword ( "void" ) ) {
233- this . skipWhitespace ( ) ;
234- const argument = this . parseUnary ( ) ;
235- return {
236- type : "UnaryExpr" ,
237- operator : "void" ,
238- argument,
239- prefix : true ,
240- } ;
221+ // Keyword unary operators
222+ for ( const keyword of [ "typeof" , "void" ] as const ) {
223+ if ( this . matchKeyword ( keyword ) ) {
224+ this . skipWhitespace ( ) ;
225+ return {
226+ type : "UnaryExpr" ,
227+ operator : keyword ,
228+ argument : this . parseUnary ( ) ,
229+ prefix : true ,
230+ } ;
231+ }
241232 }
242233
243234 return this . parsePostfix ( ) ;
@@ -438,6 +429,16 @@ class Parser {
438429 } ;
439430 }
440431
432+ private static readonly ESCAPE_CHARS : Record < string , string > = {
433+ n : "\n" ,
434+ r : "\r" ,
435+ t : "\t" ,
436+ "\\" : "\\" ,
437+ "'" : "'" ,
438+ '"' : '"' ,
439+ "`" : "`" ,
440+ } ;
441+
441442 private parseString ( ) : StringLiteral {
442443 const quote = this . peek ( ) as "'" | '"' | "`" ;
443444 this . advance ( ) ;
@@ -447,31 +448,7 @@ class Parser {
447448 if ( this . peek ( ) === "\\" ) {
448449 this . advance ( ) ;
449450 const escaped = this . peek ( ) ;
450- switch ( escaped ) {
451- case "n" :
452- value += "\n" ;
453- break ;
454- case "r" :
455- value += "\r" ;
456- break ;
457- case "t" :
458- value += "\t" ;
459- break ;
460- case "\\" :
461- value += "\\" ;
462- break ;
463- case "'" :
464- value += "'" ;
465- break ;
466- case '"' :
467- value += '"' ;
468- break ;
469- case "`" :
470- value += "`" ;
471- break ;
472- default :
473- value += escaped ;
474- }
451+ value += Parser . ESCAPE_CHARS [ escaped ] ?? escaped ;
475452 this . advance ( ) ;
476453 } else {
477454 value += this . peek ( ) ;
@@ -585,47 +562,52 @@ class Parser {
585562 return args ;
586563 }
587564
565+ // Operators sorted by length (longest first) to ensure correct matching
566+ private static readonly OPERATORS = [
567+ // 10 chars
568+ "instanceof" ,
569+ // 3 chars
570+ ">>>" ,
571+ "===" ,
572+ "!==" ,
573+ // 2 chars
574+ "&&" ,
575+ "||" ,
576+ "??" ,
577+ "==" ,
578+ "!=" ,
579+ "<=" ,
580+ ">=" ,
581+ "<<" ,
582+ ">>" ,
583+ "**" ,
584+ "in" ,
585+ // 1 char
586+ "+" ,
587+ "-" ,
588+ "*" ,
589+ "/" ,
590+ "%" ,
591+ "<" ,
592+ ">" ,
593+ "&" ,
594+ "|" ,
595+ "^" ,
596+ ] ;
597+
598+ private static readonly KEYWORD_OPERATORS = new Set ( [ "in" , "instanceof" ] ) ;
599+
588600 private peekOperator ( ) : string | null {
589- // 按长度排序,先匹配长的运算符
590- const ops = [
591- ">>>" ,
592- "===" ,
593- "!==" ,
594- "instanceof" ,
595- "&&" ,
596- "||" ,
597- "??" ,
598- "==" ,
599- "!=" ,
600- "<=" ,
601- ">=" ,
602- "<<" ,
603- ">>" ,
604- "**" ,
605- "in" ,
606- "+" ,
607- "-" ,
608- "*" ,
609- "/" ,
610- "%" ,
611- "<" ,
612- ">" ,
613- "&" ,
614- "|" ,
615- "^" ,
616- ] ;
617-
618- for ( const op of ops ) {
619- if ( this . source . startsWith ( op , this . pos ) ) {
620- // 对于 "in" 和 "instanceof",确保后面不是标识符字符
621- if ( op === "in" || op === "instanceof" ) {
622- const nextChar = this . source [ this . pos + op . length ] ;
623- if ( nextChar && this . isIdentifierPart ( nextChar ) ) {
624- continue ;
625- }
626- }
627- return op ;
601+ for ( const op of Parser . OPERATORS ) {
602+ if ( ! this . source . startsWith ( op , this . pos ) ) continue ;
603+
604+ // Keyword operators must not be followed by identifier characters
605+ if ( Parser . KEYWORD_OPERATORS . has ( op ) ) {
606+ const nextChar = this . source [ this . pos + op . length ] ;
607+ if ( nextChar && this . isIdentifierPart ( nextChar ) ) continue ;
628608 }
609+
610+ return op ;
629611 }
630612 return null ;
631613 }
@@ -667,19 +649,29 @@ class Parser {
667649 }
668650
669651 private isDigit ( ch : string ) : boolean {
670- return / [ 0 - 9 ] / . test ( ch ) ;
652+ const code = ch . charCodeAt ( 0 ) ;
653+ return code >= 48 && code <= 57 ; // 0-9
671654 }
672655
673656 private isHexDigit ( ch : string ) : boolean {
674- return / [ 0 - 9 a - f A - F ] / . test ( ch ) ;
657+ const code = ch . charCodeAt ( 0 ) ;
658+ return ( code >= 48 && code <= 57 ) || ( code >= 65 && code <= 70 ) || ( code >= 97 && code <= 102 ) ;
675659 }
676660
677661 private isIdentifierStart ( ch : string ) : boolean {
678- return / [ a - z A - Z _ $ ] / . test ( ch ) ;
662+ const code = ch . charCodeAt ( 0 ) ;
663+ return ( code >= 65 && code <= 90 ) || ( code >= 97 && code <= 122 ) || code === 95 || code === 36 ;
679664 }
680665
681666 private isIdentifierPart ( ch : string ) : boolean {
682- return / [ a - z A - Z 0 - 9 _ $ ] / . test ( ch ) ;
667+ const code = ch . charCodeAt ( 0 ) ;
668+ return (
669+ ( code >= 65 && code <= 90 ) ||
670+ ( code >= 97 && code <= 122 ) ||
671+ ( code >= 48 && code <= 57 ) ||
672+ code === 95 ||
673+ code === 36
674+ ) ;
683675 }
684676}
685677
0 commit comments