| 162 | } StringMode; |
| 163 | |
| 164 | static bool scan_string_content(TSLexer *lexer, bool is_multiline, StringMode string_mode) { |
| 165 | LOG("scan_string_content(%d, %d, %c)\n", is_multiline, string_mode, lexer->lookahead); |
| 166 | unsigned closing_quote_count = 0; |
| 167 | for (;;) { |
| 168 | if (lexer->lookahead == '"') { |
| 169 | advance(lexer); |
| 170 | closing_quote_count++; |
| 171 | if (!is_multiline) { |
| 172 | lexer->result_symbol = SINGLE_LINE_STRING_END; |
| 173 | lexer->mark_end(lexer); |
| 174 | return true; |
| 175 | } |
| 176 | if (closing_quote_count >= 3 && lexer->lookahead != '"') { |
| 177 | lexer->result_symbol = MULTILINE_STRING_END; |
| 178 | lexer->mark_end(lexer); |
| 179 | return true; |
| 180 | } |
| 181 | } else if (lexer->lookahead == '$' && string_mode != STRING_MODE_SIMPLE) { |
| 182 | switch (string_mode) { |
| 183 | case STRING_MODE_INTERPOLATED: |
| 184 | lexer->result_symbol = is_multiline ? INTERPOLATED_MULTILINE_STRING_MIDDLE : INTERPOLATED_STRING_MIDDLE; |
| 185 | break; |
| 186 | case STRING_MODE_RAW: |
| 187 | lexer->result_symbol = is_multiline ? RAW_STRING_MULTILINE_MIDDLE : RAW_STRING_MIDDLE; |
| 188 | break; |
| 189 | default: |
| 190 | assert(false); |
| 191 | } |
| 192 | lexer->mark_end(lexer); |
| 193 | return true; |
| 194 | } else { |
| 195 | closing_quote_count = 0; |
| 196 | if (lexer->lookahead == '\\') { |
| 197 | // Multiline strings ignore escape sequences |
| 198 | if (is_multiline || string_mode == STRING_MODE_RAW) { |
| 199 | // FIXME: In raw string mode, we have to jump over escaped quotes. |
| 200 | advance(lexer); |
| 201 | // In single-line raw strings, `\"` is not translated to `"`, but it also does |
| 202 | // not close the string. Likewise, `\\` is not translated to `\`, but it does |
| 203 | // stop the second `\` from stopping a double-quote from closing the string. |
| 204 | if (!is_multiline && string_mode == STRING_MODE_RAW && |
| 205 | (lexer->lookahead == '"' || lexer->lookahead == '\\')) { |
| 206 | advance(lexer); |
| 207 | } |
| 208 | } else { |
| 209 | lexer->result_symbol = string_mode == STRING_MODE_SIMPLE ? SIMPLE_STRING_MIDDLE : INTERPOLATED_STRING_MIDDLE; |
| 210 | lexer->mark_end(lexer); |
| 211 | return true; |
| 212 | } |
| 213 | // During error recovery and dynamic precedence resolution, the external |
| 214 | // scanner will be invoked with all valid_symbols set to true, which means |
| 215 | // we will be asked to scan a string token when we are not actually in a |
| 216 | // string context. Here we detect these cases and return false. |
| 217 | } else if (lexer->lookahead == '\n' && !is_multiline) { |
| 218 | return false; |
| 219 | } else if (lexer->eof(lexer)) { |
| 220 | return false; |
| 221 | } else { |
no test coverage detected