()
| 959 | // https://tc39.github.io/ecma262/#sec-template-literal-lexical-components |
| 960 | |
| 961 | private scanTemplate(): RawToken { |
| 962 | let cooked = ''; |
| 963 | let terminated = false; |
| 964 | const start = this.index; |
| 965 | |
| 966 | const head = (this.source[start] === '`'); |
| 967 | let tail = false; |
| 968 | let rawOffset = 2; |
| 969 | |
| 970 | ++this.index; |
| 971 | |
| 972 | while (!this.eof()) { |
| 973 | let ch = this.source[this.index++]; |
| 974 | if (ch === '`') { |
| 975 | rawOffset = 1; |
| 976 | tail = true; |
| 977 | terminated = true; |
| 978 | break; |
| 979 | } else if (ch === '$') { |
| 980 | if (this.source[this.index] === '{') { |
| 981 | this.curlyStack.push('${'); |
| 982 | ++this.index; |
| 983 | terminated = true; |
| 984 | break; |
| 985 | } |
| 986 | cooked += ch; |
| 987 | } else if (ch === '\\') { |
| 988 | ch = this.source[this.index++]; |
| 989 | if (!Character.isLineTerminator(ch.charCodeAt(0))) { |
| 990 | switch (ch) { |
| 991 | case 'n': |
| 992 | cooked += '\n'; |
| 993 | break; |
| 994 | case 'r': |
| 995 | cooked += '\r'; |
| 996 | break; |
| 997 | case 't': |
| 998 | cooked += '\t'; |
| 999 | break; |
| 1000 | case 'u': |
| 1001 | if (this.source[this.index] === '{') { |
| 1002 | ++this.index; |
| 1003 | cooked += this.scanUnicodeCodePointEscape(); |
| 1004 | } else { |
| 1005 | const restore = this.index; |
| 1006 | const unescaped = this.scanHexEscape(ch); |
| 1007 | if (unescaped !== null) { |
| 1008 | cooked += unescaped; |
| 1009 | } else { |
| 1010 | this.index = restore; |
| 1011 | cooked += ch; |
| 1012 | } |
| 1013 | } |
| 1014 | break; |
| 1015 | case 'x': |
| 1016 | const unescaped = this.scanHexEscape(ch); |
| 1017 | if (unescaped === null) { |
| 1018 | this.throwUnexpectedToken(Messages.InvalidHexEscapeSequence); |
no test coverage detected