| ... | ... | @@ -223,6 +223,42 @@ static uint8_t parse_char_literal(ParseContext *pc, Token *token) { |
| 223 | 223 | return return_value; |
| 224 | 224 | } |
| 225 | 225 | |
| 226 | static int get_hex_digit(uint8_t c) { |
| 227 | switch (c) { |
| 228 | case '0': return 0; |
| 229 | case '1': return 1; |
| 230 | case '2': return 2; |
| 231 | case '3': return 3; |
| 232 | case '4': return 4; |
| 233 | case '5': return 5; |
| 234 | case '6': return 6; |
| 235 | case '7': return 7; |
| 236 | case '8': return 8; |
| 237 | case '9': return 9; |
| 238 | |
| 239 | case 'a': |
| 240 | case 'A': |
| 241 | return 10; |
| 242 | case 'b': |
| 243 | case 'B': |
| 244 | return 11; |
| 245 | case 'c': |
| 246 | case 'C': |
| 247 | return 12; |
| 248 | case 'd': |
| 249 | case 'D': |
| 250 | return 13; |
| 251 | case 'e': |
| 252 | case 'E': |
| 253 | return 14; |
| 254 | case 'f': |
| 255 | case 'F': |
| 256 | return 15; |
| 257 | default: |
| 258 | return -1; |
| 259 | } |
| 260 | } |
| 261 | |
| 226 | 262 | static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, bool *out_c_str, |
| 227 | 263 | ZigList<SrcPos> *offset_map) |
| 228 | 264 | { |
| ... | ... | @@ -240,12 +276,22 @@ static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, bool |
| 240 | 276 | // convert escape sequences |
| 241 | 277 | // detect c string literal |
| 242 | 278 | |
| 279 | enum State { |
| 280 | StateStart, |
| 281 | StateEscape, |
| 282 | StateHex1, |
| 283 | StateHex2, |
| 284 | }; |
| 285 | |
| 243 | 286 | buf_resize(buf, 0); |
| 244 | | bool escape = false; |
| 287 | |
| 288 | State state = StateStart; |
| 245 | 289 | bool skip_quote; |
| 246 | 290 | SrcPos pos = {token->start_line, token->start_column}; |
| 291 | int hex_value = 0; |
| 247 | 292 | for (int i = token->start_pos; i < token->end_pos - 1; i += 1) { |
| 248 | 293 | uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i); |
| 294 | |
| 249 | 295 | if (i == token->start_pos) { |
| 250 | 296 | skip_quote = (c == 'c'); |
| 251 | 297 | if (out_c_str) { |
| ... | ... | @@ -256,41 +302,72 @@ static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, bool |
| 256 | 302 | } else if (skip_quote) { |
| 257 | 303 | skip_quote = false; |
| 258 | 304 | } else { |
| 259 | | if (escape) { |
| 260 | | switch (c) { |
| 261 | | case '\\': |
| 262 | | buf_append_char(buf, '\\'); |
| 263 | | if (offset_map) offset_map->append(pos); |
| 264 | | break; |
| 265 | | case 'r': |
| 266 | | buf_append_char(buf, '\r'); |
| 267 | | if (offset_map) offset_map->append(pos); |
| 268 | | break; |
| 269 | | case 'n': |
| 270 | | buf_append_char(buf, '\n'); |
| 271 | | if (offset_map) offset_map->append(pos); |
| 272 | | break; |
| 273 | | case 't': |
| 274 | | buf_append_char(buf, '\t'); |
| 275 | | if (offset_map) offset_map->append(pos); |
| 276 | | break; |
| 277 | | case '"': |
| 278 | | buf_append_char(buf, '"'); |
| 305 | switch (state) { |
| 306 | case StateStart: |
| 307 | if (c == '\\') { |
| 308 | state = StateEscape; |
| 309 | } else { |
| 310 | buf_append_char(buf, c); |
| 279 | 311 | if (offset_map) offset_map->append(pos); |
| 312 | } |
| 313 | break; |
| 314 | case StateEscape: |
| 315 | switch (c) { |
| 316 | case '\\': |
| 317 | buf_append_char(buf, '\\'); |
| 318 | if (offset_map) offset_map->append(pos); |
| 319 | state = StateStart; |
| 320 | break; |
| 321 | case 'r': |
| 322 | buf_append_char(buf, '\r'); |
| 323 | if (offset_map) offset_map->append(pos); |
| 324 | state = StateStart; |
| 325 | break; |
| 326 | case 'n': |
| 327 | buf_append_char(buf, '\n'); |
| 328 | if (offset_map) offset_map->append(pos); |
| 329 | state = StateStart; |
| 330 | break; |
| 331 | case 't': |
| 332 | buf_append_char(buf, '\t'); |
| 333 | if (offset_map) offset_map->append(pos); |
| 334 | state = StateStart; |
| 335 | break; |
| 336 | case '"': |
| 337 | buf_append_char(buf, '"'); |
| 338 | if (offset_map) offset_map->append(pos); |
| 339 | state = StateStart; |
| 340 | break; |
| 341 | case 'x': |
| 342 | state = StateHex1; |
| 343 | break; |
| 344 | default: |
| 345 | ast_error(pc, token, "invalid escape character"); |
| 346 | break; |
| 347 | } |
| 348 | break; |
| 349 | case StateHex1: |
| 350 | { |
| 351 | int hex_digit = get_hex_digit(c); |
| 352 | if (hex_digit == -1) { |
| 353 | ast_error(pc, token, "invalid hex digit: '%c'", c); |
| 354 | } |
| 355 | hex_value = hex_digit * 16; |
| 356 | state = StateHex2; |
| 280 | 357 | break; |
| 281 | | case 'x': |
| 282 | | zig_panic("TODO"); |
| 283 | | break; |
| 284 | | default: |
| 285 | | ast_error(pc, token, "invalid escape character"); |
| 358 | } |
| 359 | case StateHex2: |
| 360 | { |
| 361 | int hex_digit = get_hex_digit(c); |
| 362 | if (hex_digit == -1) { |
| 363 | ast_error(pc, token, "invalid hex digit: '%c'", c); |
| 364 | } |
| 365 | hex_value += hex_digit; |
| 366 | assert(hex_value >= 0 && hex_value <= 255); |
| 367 | buf_append_char(buf, hex_value); |
| 368 | state = StateStart; |
| 286 | 369 | break; |
| 287 | | } |
| 288 | | escape = false; |
| 289 | | } else if (c == '\\') { |
| 290 | | escape = true; |
| 291 | | } else { |
| 292 | | buf_append_char(buf, c); |
| 293 | | if (offset_map) offset_map->append(pos); |
| 370 | } |
| 294 | 371 | } |
| 295 | 372 | } |
| 296 | 373 | if (c == '\n') { |
| ... | ... | @@ -300,7 +377,7 @@ static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, bool |
| 300 | 377 | pos.column += 1; |
| 301 | 378 | } |
| 302 | 379 | } |
| 303 | | assert(!escape); |
| 380 | assert(state == StateStart); |
| 304 | 381 | if (offset_map) offset_map->append(pos); |
| 305 | 382 | } |
| 306 | 383 | |