()
| 112 | } |
| 113 | |
| 114 | private parseString(): string { |
| 115 | const quote = this.currentChar(); |
| 116 | if (quote !== '"' && quote !== "'") { |
| 117 | throw this.error(`String should start with a quote, got '${quote}'`); |
| 118 | } |
| 119 | this.index++; // consume opening quote |
| 120 | const result: string[] = []; |
| 121 | while (!this.isAtEnd()) { |
| 122 | const ch = this.currentChar(); |
| 123 | if (ch === quote) { |
| 124 | this.index++; // consume closing quote |
| 125 | return result.join(''); |
| 126 | } |
| 127 | if (ch === '\\') { |
| 128 | this.index++; // consume backslash |
| 129 | if (this.isAtEnd()) { |
| 130 | throw this.error('Unterminated escape sequence in string'); |
| 131 | } |
| 132 | const esc = this.currentChar(); |
| 133 | switch (esc) { |
| 134 | case 'b': |
| 135 | result.push('\b'); |
| 136 | break; |
| 137 | case 'f': |
| 138 | result.push('\f'); |
| 139 | break; |
| 140 | case 'n': |
| 141 | result.push('\n'); |
| 142 | break; |
| 143 | case 'r': |
| 144 | result.push('\r'); |
| 145 | break; |
| 146 | case 't': |
| 147 | result.push('\t'); |
| 148 | break; |
| 149 | case 'v': |
| 150 | result.push('\v'); |
| 151 | break; |
| 152 | case '\\': |
| 153 | result.push('\\'); |
| 154 | break; |
| 155 | case "'": |
| 156 | result.push("'"); |
| 157 | break; |
| 158 | case '"': |
| 159 | result.push('"'); |
| 160 | break; |
| 161 | case '0': |
| 162 | result.push('\0'); |
| 163 | break; |
| 164 | case 'u': { |
| 165 | this.index++; // consume 'u' |
| 166 | const hex = this.text.substr(this.index, 4); |
| 167 | if (!/^[0-9a-fA-F]{4}$/.test(hex)) { |
| 168 | throw this.error(`Invalid Unicode escape sequence: \\u${hex}`); |
| 169 | } |
| 170 | const codeUnit = parseInt(hex, 16); |
| 171 | this.index += 3; // already incremented once after reading esc |
no test coverage detected