1 /* 2 * Copyright 2008-2009 Katholieke Universiteit Leuven 3 * 4 * Use of this software is governed by the MIT license 5 * 6 * Written by Sven Verdoolaege, K.U.Leuven, Departement 7 * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium 8 */ 9 10 #include <ctype.h> 11 #include <string.h> 12 #include <isl_ctx_private.h> 13 #include <isl_stream_private.h> 14 #include <isl/map.h> 15 #include <isl/aff.h> 16 #include <isl_val_private.h> 17 #include <isl_options_private.h> 18 19 struct isl_keyword { 20 char *name; 21 enum isl_token_type type; 22 }; 23 24 static isl_bool same_name(const void *entry, const void *val) 25 { 26 const struct isl_keyword *keyword = (const struct isl_keyword *)entry; 27 28 return isl_bool_ok(!strcmp(keyword->name, val)); 29 } 30 31 enum isl_token_type isl_stream_register_keyword(__isl_keep isl_stream *s, 32 const char *name) 33 { 34 struct isl_hash_table_entry *entry; 35 struct isl_keyword *keyword; 36 uint32_t name_hash; 37 38 if (!s->keywords) { 39 s->keywords = isl_hash_table_alloc(s->ctx, 10); 40 if (!s->keywords) 41 return ISL_TOKEN_ERROR; 42 s->next_type = ISL_TOKEN_LAST; 43 } 44 45 name_hash = isl_hash_string(isl_hash_init(), name); 46 47 entry = isl_hash_table_find(s->ctx, s->keywords, name_hash, 48 same_name, name, 1); 49 if (!entry) 50 return ISL_TOKEN_ERROR; 51 if (entry->data) { 52 keyword = entry->data; 53 return keyword->type; 54 } 55 56 keyword = isl_calloc_type(s->ctx, struct isl_keyword); 57 if (!keyword) 58 return ISL_TOKEN_ERROR; 59 keyword->type = s->next_type++; 60 keyword->name = strdup(name); 61 if (!keyword->name) { 62 free(keyword); 63 return ISL_TOKEN_ERROR; 64 } 65 entry->data = keyword; 66 67 return keyword->type; 68 } 69 70 struct isl_token *isl_token_new(isl_ctx *ctx, 71 int line, int col, unsigned on_new_line) 72 { 73 struct isl_token *tok = isl_alloc_type(ctx, struct isl_token); 74 if (!tok) 75 return NULL; 76 tok->line = line; 77 tok->col = col; 78 tok->on_new_line = on_new_line; 79 tok->is_keyword = 0; 80 tok->u.s = NULL; 81 return tok; 82 } 83 84 /* Return the type of "tok". 85 */ 86 int isl_token_get_type(struct isl_token *tok) 87 { 88 return tok ? tok->type : ISL_TOKEN_ERROR; 89 } 90 91 /* Given a token of type ISL_TOKEN_VALUE, return the value it represents. 92 */ 93 __isl_give isl_val *isl_token_get_val(isl_ctx *ctx, struct isl_token *tok) 94 { 95 if (!tok) 96 return NULL; 97 if (tok->type != ISL_TOKEN_VALUE) 98 isl_die(ctx, isl_error_invalid, "not a value token", 99 return NULL); 100 101 return isl_val_int_from_isl_int(ctx, tok->u.v); 102 } 103 104 /* Does the given token have a string representation? 105 */ 106 isl_bool isl_token_has_str(struct isl_token *tok) 107 { 108 if (!tok) 109 return isl_bool_error; 110 return isl_bool_ok(tok->u.s != NULL); 111 } 112 113 /* Given a token with a string representation, return a copy of this string. 114 */ 115 __isl_give char *isl_token_get_str(isl_ctx *ctx, struct isl_token *tok) 116 { 117 if (!tok) 118 return NULL; 119 if (!tok->u.s) 120 isl_die(ctx, isl_error_invalid, 121 "token does not have a string representation", 122 return NULL); 123 124 return strdup(tok->u.s); 125 } 126 127 void isl_token_free(struct isl_token *tok) 128 { 129 if (!tok) 130 return; 131 if (tok->type == ISL_TOKEN_VALUE) 132 isl_int_clear(tok->u.v); 133 else if (tok->type == ISL_TOKEN_MAP) 134 isl_map_free(tok->u.map); 135 else if (tok->type == ISL_TOKEN_AFF) 136 isl_pw_aff_free(tok->u.pwaff); 137 else 138 free(tok->u.s); 139 free(tok); 140 } 141 142 void isl_stream_error(__isl_keep isl_stream *s, struct isl_token *tok, 143 char *msg) 144 { 145 int line = tok ? tok->line : s->line; 146 int col = tok ? tok->col : s->col; 147 148 isl_ctx_set_full_error(s->ctx, isl_error_invalid, "syntax error", 149 __FILE__, __LINE__); 150 151 if (s->ctx->opt->on_error == ISL_ON_ERROR_CONTINUE) 152 return; 153 fprintf(stderr, "syntax error (%d, %d): %s\n", line, col, msg); 154 if (tok) { 155 if (tok->type < 256) 156 fprintf(stderr, "got '%c'\n", tok->type); 157 else if (tok->type == ISL_TOKEN_IDENT) 158 fprintf(stderr, "got ident '%s'\n", tok->u.s); 159 else if (tok->is_keyword) 160 fprintf(stderr, "got keyword '%s'\n", tok->u.s); 161 else if (tok->type == ISL_TOKEN_VALUE) { 162 fprintf(stderr, "got value '"); 163 isl_int_print(stderr, tok->u.v, 0); 164 fprintf(stderr, "'\n"); 165 } else if (tok->type == ISL_TOKEN_MAP) { 166 isl_printer *p; 167 fprintf(stderr, "got map '"); 168 p = isl_printer_to_file(s->ctx, stderr); 169 p = isl_printer_print_map(p, tok->u.map); 170 isl_printer_free(p); 171 fprintf(stderr, "'\n"); 172 } else if (tok->type == ISL_TOKEN_AFF) { 173 isl_printer *p; 174 fprintf(stderr, "got affine expression '"); 175 p = isl_printer_to_file(s->ctx, stderr); 176 p = isl_printer_print_pw_aff(p, tok->u.pwaff); 177 isl_printer_free(p); 178 fprintf(stderr, "'\n"); 179 } else if (tok->u.s) 180 fprintf(stderr, "got token '%s'\n", tok->u.s); 181 else 182 fprintf(stderr, "got token type %d\n", tok->type); 183 } 184 if (s->ctx->opt->on_error == ISL_ON_ERROR_ABORT) 185 abort(); 186 } 187 188 static __isl_give isl_stream* isl_stream_new(struct isl_ctx *ctx) 189 { 190 int i; 191 isl_stream *s = isl_calloc_type(ctx, struct isl_stream); 192 if (!s) 193 return NULL; 194 s->ctx = ctx; 195 isl_ctx_ref(s->ctx); 196 s->file = NULL; 197 s->str = NULL; 198 s->len = 0; 199 s->line = 1; 200 s->col = 1; 201 s->eof = 0; 202 s->last_line = 0; 203 s->c = -1; 204 s->n_un = 0; 205 for (i = 0; i < 5; ++i) 206 s->tokens[i] = NULL; 207 s->n_token = 0; 208 s->keywords = NULL; 209 s->size = 256; 210 s->buffer = isl_alloc_array(ctx, char, s->size); 211 if (!s->buffer) 212 goto error; 213 return s; 214 error: 215 isl_stream_free(s); 216 return NULL; 217 } 218 219 __isl_give isl_stream* isl_stream_new_file(struct isl_ctx *ctx, FILE *file) 220 { 221 isl_stream *s = isl_stream_new(ctx); 222 if (!s) 223 return NULL; 224 s->file = file; 225 return s; 226 } 227 228 __isl_give isl_stream* isl_stream_new_str(struct isl_ctx *ctx, const char *str) 229 { 230 isl_stream *s; 231 if (!str) 232 return NULL; 233 s = isl_stream_new(ctx); 234 if (!s) 235 return NULL; 236 s->str = str; 237 return s; 238 } 239 240 /* Read a character from the stream and advance s->line and s->col 241 * to point to the next character. 242 */ 243 static int stream_getc(__isl_keep isl_stream *s) 244 { 245 int c; 246 if (s->eof) 247 return -1; 248 if (s->n_un) 249 return s->c = s->un[--s->n_un]; 250 if (s->file) 251 c = fgetc(s->file); 252 else { 253 c = *s->str++; 254 if (c == '\0') 255 c = -1; 256 } 257 if (c == -1) 258 s->eof = 1; 259 else if (c == '\n') { 260 s->line++; 261 s->col = 1; 262 } else 263 s->col++; 264 s->c = c; 265 return c; 266 } 267 268 static void isl_stream_ungetc(__isl_keep isl_stream *s, int c) 269 { 270 isl_assert(s->ctx, s->n_un < 5, return); 271 s->un[s->n_un++] = c; 272 s->c = -1; 273 } 274 275 /* Read a character from the stream, skipping pairs of '\\' and '\n'. 276 * Set s->start_line and s->start_col to the line and column 277 * of the returned character. 278 */ 279 static int isl_stream_getc(__isl_keep isl_stream *s) 280 { 281 int c; 282 283 do { 284 s->start_line = s->line; 285 s->start_col = s->col; 286 c = stream_getc(s); 287 if (c != '\\') 288 return c; 289 c = stream_getc(s); 290 } while (c == '\n'); 291 292 isl_stream_ungetc(s, c); 293 294 return '\\'; 295 } 296 297 static int isl_stream_push_char(__isl_keep isl_stream *s, int c) 298 { 299 if (s->len >= s->size) { 300 char *buffer; 301 s->size = (3*s->size)/2; 302 buffer = isl_realloc_array(s->ctx, s->buffer, char, s->size); 303 if (!buffer) 304 return -1; 305 s->buffer = buffer; 306 } 307 s->buffer[s->len++] = c; 308 return 0; 309 } 310 311 void isl_stream_push_token(__isl_keep isl_stream *s, struct isl_token *tok) 312 { 313 isl_assert(s->ctx, s->n_token < 5, return); 314 s->tokens[s->n_token++] = tok; 315 } 316 317 static enum isl_token_type check_keywords(__isl_keep isl_stream *s) 318 { 319 struct isl_hash_table_entry *entry; 320 struct isl_keyword *keyword; 321 uint32_t name_hash; 322 323 if (!strcasecmp(s->buffer, "exists")) 324 return ISL_TOKEN_EXISTS; 325 if (!strcasecmp(s->buffer, "and")) 326 return ISL_TOKEN_AND; 327 if (!strcasecmp(s->buffer, "or")) 328 return ISL_TOKEN_OR; 329 if (!strcasecmp(s->buffer, "implies")) 330 return ISL_TOKEN_IMPLIES; 331 if (!strcasecmp(s->buffer, "not")) 332 return ISL_TOKEN_NOT; 333 if (!strcasecmp(s->buffer, "infty")) 334 return ISL_TOKEN_INFTY; 335 if (!strcasecmp(s->buffer, "infinity")) 336 return ISL_TOKEN_INFTY; 337 if (!strcasecmp(s->buffer, "NaN")) 338 return ISL_TOKEN_NAN; 339 if (!strcasecmp(s->buffer, "min")) 340 return ISL_TOKEN_MIN; 341 if (!strcasecmp(s->buffer, "max")) 342 return ISL_TOKEN_MAX; 343 if (!strcasecmp(s->buffer, "rat")) 344 return ISL_TOKEN_RAT; 345 if (!strcasecmp(s->buffer, "true")) 346 return ISL_TOKEN_TRUE; 347 if (!strcasecmp(s->buffer, "false")) 348 return ISL_TOKEN_FALSE; 349 if (!strcasecmp(s->buffer, "ceild")) 350 return ISL_TOKEN_CEILD; 351 if (!strcasecmp(s->buffer, "floord")) 352 return ISL_TOKEN_FLOORD; 353 if (!strcasecmp(s->buffer, "mod")) 354 return ISL_TOKEN_MOD; 355 if (!strcasecmp(s->buffer, "ceil")) 356 return ISL_TOKEN_CEIL; 357 if (!strcasecmp(s->buffer, "floor")) 358 return ISL_TOKEN_FLOOR; 359 360 if (!s->keywords) 361 return ISL_TOKEN_IDENT; 362 363 name_hash = isl_hash_string(isl_hash_init(), s->buffer); 364 entry = isl_hash_table_find(s->ctx, s->keywords, name_hash, same_name, 365 s->buffer, 0); 366 if (!entry) 367 return ISL_TOKEN_ERROR; 368 if (entry != isl_hash_table_entry_none) { 369 keyword = entry->data; 370 return keyword->type; 371 } 372 373 return ISL_TOKEN_IDENT; 374 } 375 376 int isl_stream_skip_line(__isl_keep isl_stream *s) 377 { 378 int c; 379 380 while ((c = isl_stream_getc(s)) != -1 && c != '\n') 381 /* nothing */ 382 ; 383 384 return c == -1 ? -1 : 0; 385 } 386 387 static struct isl_token *next_token(__isl_keep isl_stream *s, int same_line) 388 { 389 int c; 390 struct isl_token *tok = NULL; 391 int line, col; 392 int old_line = s->last_line; 393 394 if (s->n_token) { 395 if (same_line && s->tokens[s->n_token - 1]->on_new_line) 396 return NULL; 397 return s->tokens[--s->n_token]; 398 } 399 400 if (same_line && s->c == '\n') 401 return NULL; 402 403 s->len = 0; 404 405 /* skip spaces and comment lines */ 406 while ((c = isl_stream_getc(s)) != -1) { 407 if (c == '#') { 408 if (isl_stream_skip_line(s) < 0) 409 break; 410 c = '\n'; 411 if (same_line) 412 break; 413 } else if (!isspace(c) || (same_line && c == '\n')) 414 break; 415 } 416 417 line = s->start_line; 418 col = s->start_col; 419 420 if (c == -1 || (same_line && c == '\n')) 421 return NULL; 422 s->last_line = line; 423 424 if (c == '(' || 425 c == ')' || 426 c == '+' || 427 c == '*' || 428 c == '%' || 429 c == '?' || 430 c == '^' || 431 c == '@' || 432 c == '$' || 433 c == ',' || 434 c == '.' || 435 c == ';' || 436 c == '[' || 437 c == ']' || 438 c == '{' || 439 c == '}') { 440 tok = isl_token_new(s->ctx, line, col, old_line != line); 441 if (!tok) 442 return NULL; 443 tok->type = (enum isl_token_type)c; 444 return tok; 445 } 446 if (c == '-') { 447 int c; 448 if ((c = isl_stream_getc(s)) == '>') { 449 tok = isl_token_new(s->ctx, line, col, old_line != line); 450 if (!tok) 451 return NULL; 452 tok->u.s = strdup("->"); 453 tok->type = ISL_TOKEN_TO; 454 return tok; 455 } 456 if (c != -1) 457 isl_stream_ungetc(s, c); 458 tok = isl_token_new(s->ctx, line, col, old_line != line); 459 if (!tok) 460 return NULL; 461 tok->type = (enum isl_token_type) '-'; 462 return tok; 463 } 464 if (isdigit(c)) { 465 int minus = c == '-'; 466 tok = isl_token_new(s->ctx, line, col, old_line != line); 467 if (!tok) 468 return NULL; 469 tok->type = ISL_TOKEN_VALUE; 470 isl_int_init(tok->u.v); 471 if (isl_stream_push_char(s, c)) 472 goto error; 473 while ((c = isl_stream_getc(s)) != -1 && isdigit(c)) 474 if (isl_stream_push_char(s, c)) 475 goto error; 476 if (c != -1) 477 isl_stream_ungetc(s, c); 478 isl_stream_push_char(s, '\0'); 479 isl_int_read(tok->u.v, s->buffer); 480 if (minus && isl_int_is_zero(tok->u.v)) { 481 tok->col++; 482 tok->on_new_line = 0; 483 isl_stream_push_token(s, tok); 484 tok = isl_token_new(s->ctx, line, col, old_line != line); 485 if (!tok) 486 return NULL; 487 tok->type = (enum isl_token_type) '-'; 488 } 489 return tok; 490 } 491 if (isalpha(c) || c == '_') { 492 tok = isl_token_new(s->ctx, line, col, old_line != line); 493 if (!tok) 494 return NULL; 495 isl_stream_push_char(s, c); 496 while ((c = isl_stream_getc(s)) != -1 && 497 (isalnum(c) || c == '_')) 498 isl_stream_push_char(s, c); 499 if (c != -1) 500 isl_stream_ungetc(s, c); 501 while ((c = isl_stream_getc(s)) != -1 && c == '\'') 502 isl_stream_push_char(s, c); 503 if (c != -1) 504 isl_stream_ungetc(s, c); 505 isl_stream_push_char(s, '\0'); 506 tok->type = check_keywords(s); 507 if (tok->type != ISL_TOKEN_IDENT) 508 tok->is_keyword = 1; 509 tok->u.s = strdup(s->buffer); 510 if (!tok->u.s) 511 goto error; 512 return tok; 513 } 514 if (c == '"') { 515 tok = isl_token_new(s->ctx, line, col, old_line != line); 516 if (!tok) 517 return NULL; 518 tok->type = ISL_TOKEN_STRING; 519 tok->u.s = NULL; 520 while ((c = isl_stream_getc(s)) != -1 && c != '"' && c != '\n') 521 isl_stream_push_char(s, c); 522 if (c != '"') { 523 isl_stream_error(s, NULL, "unterminated string"); 524 goto error; 525 } 526 isl_stream_push_char(s, '\0'); 527 tok->u.s = strdup(s->buffer); 528 return tok; 529 } 530 if (c == '=') { 531 int c; 532 tok = isl_token_new(s->ctx, line, col, old_line != line); 533 if (!tok) 534 return NULL; 535 if ((c = isl_stream_getc(s)) == '=') { 536 tok->u.s = strdup("=="); 537 tok->type = ISL_TOKEN_EQ_EQ; 538 return tok; 539 } 540 if (c != -1) 541 isl_stream_ungetc(s, c); 542 tok->type = (enum isl_token_type) '='; 543 return tok; 544 } 545 if (c == ':') { 546 int c; 547 tok = isl_token_new(s->ctx, line, col, old_line != line); 548 if (!tok) 549 return NULL; 550 if ((c = isl_stream_getc(s)) == '=') { 551 tok->u.s = strdup(":="); 552 tok->type = ISL_TOKEN_DEF; 553 return tok; 554 } 555 if (c != -1) 556 isl_stream_ungetc(s, c); 557 tok->type = (enum isl_token_type) ':'; 558 return tok; 559 } 560 if (c == '>') { 561 int c; 562 tok = isl_token_new(s->ctx, line, col, old_line != line); 563 if (!tok) 564 return NULL; 565 if ((c = isl_stream_getc(s)) == '=') { 566 tok->u.s = strdup(">="); 567 tok->type = ISL_TOKEN_GE; 568 return tok; 569 } else if (c == '>') { 570 if ((c = isl_stream_getc(s)) == '=') { 571 tok->u.s = strdup(">>="); 572 tok->type = ISL_TOKEN_LEX_GE; 573 return tok; 574 } 575 tok->u.s = strdup(">>"); 576 tok->type = ISL_TOKEN_LEX_GT; 577 } else { 578 tok->u.s = strdup(">"); 579 tok->type = ISL_TOKEN_GT; 580 } 581 if (c != -1) 582 isl_stream_ungetc(s, c); 583 return tok; 584 } 585 if (c == '<') { 586 int c; 587 tok = isl_token_new(s->ctx, line, col, old_line != line); 588 if (!tok) 589 return NULL; 590 if ((c = isl_stream_getc(s)) == '=') { 591 tok->u.s = strdup("<="); 592 tok->type = ISL_TOKEN_LE; 593 return tok; 594 } else if (c == '<') { 595 if ((c = isl_stream_getc(s)) == '=') { 596 tok->u.s = strdup("<<="); 597 tok->type = ISL_TOKEN_LEX_LE; 598 return tok; 599 } 600 tok->u.s = strdup("<<"); 601 tok->type = ISL_TOKEN_LEX_LT; 602 } else { 603 tok->u.s = strdup("<"); 604 tok->type = ISL_TOKEN_LT; 605 } 606 if (c != -1) 607 isl_stream_ungetc(s, c); 608 return tok; 609 } 610 if (c == '&') { 611 tok = isl_token_new(s->ctx, line, col, old_line != line); 612 if (!tok) 613 return NULL; 614 tok->type = ISL_TOKEN_AND; 615 if ((c = isl_stream_getc(s)) != '&' && c != -1) { 616 tok->u.s = strdup("&"); 617 isl_stream_ungetc(s, c); 618 } else 619 tok->u.s = strdup("&&"); 620 return tok; 621 } 622 if (c == '|') { 623 tok = isl_token_new(s->ctx, line, col, old_line != line); 624 if (!tok) 625 return NULL; 626 tok->type = ISL_TOKEN_OR; 627 if ((c = isl_stream_getc(s)) != '|' && c != -1) { 628 tok->u.s = strdup("|"); 629 isl_stream_ungetc(s, c); 630 } else 631 tok->u.s = strdup("||"); 632 return tok; 633 } 634 if (c == '/') { 635 tok = isl_token_new(s->ctx, line, col, old_line != line); 636 if (!tok) 637 return NULL; 638 if ((c = isl_stream_getc(s)) == '\\') { 639 tok->u.s = strdup("/\\"); 640 tok->type = ISL_TOKEN_AND; 641 return tok; 642 } else if (c == '/') { 643 tok->u.s = strdup("//"); 644 tok->type = ISL_TOKEN_INT_DIV; 645 return tok; 646 } else { 647 tok->type = (enum isl_token_type) '/'; 648 } 649 if (c != -1) 650 isl_stream_ungetc(s, c); 651 return tok; 652 } 653 if (c == '\\') { 654 tok = isl_token_new(s->ctx, line, col, old_line != line); 655 if (!tok) 656 return NULL; 657 if ((c = isl_stream_getc(s)) != '/' && c != -1) { 658 tok->type = (enum isl_token_type) '\\'; 659 isl_stream_ungetc(s, c); 660 } else { 661 tok->u.s = strdup("\\/"); 662 tok->type = ISL_TOKEN_OR; 663 } 664 return tok; 665 } 666 if (c == '!') { 667 tok = isl_token_new(s->ctx, line, col, old_line != line); 668 if (!tok) 669 return NULL; 670 if ((c = isl_stream_getc(s)) == '=') { 671 tok->u.s = strdup("!="); 672 tok->type = ISL_TOKEN_NE; 673 return tok; 674 } else { 675 tok->type = ISL_TOKEN_NOT; 676 tok->u.s = strdup("!"); 677 } 678 if (c != -1) 679 isl_stream_ungetc(s, c); 680 return tok; 681 } 682 683 tok = isl_token_new(s->ctx, line, col, old_line != line); 684 if (!tok) 685 return NULL; 686 tok->type = ISL_TOKEN_UNKNOWN; 687 return tok; 688 error: 689 isl_token_free(tok); 690 return NULL; 691 } 692 693 struct isl_token *isl_stream_next_token(__isl_keep isl_stream *s) 694 { 695 return next_token(s, 0); 696 } 697 698 struct isl_token *isl_stream_next_token_on_same_line(__isl_keep isl_stream *s) 699 { 700 return next_token(s, 1); 701 } 702 703 int isl_stream_eat_if_available(__isl_keep isl_stream *s, int type) 704 { 705 struct isl_token *tok; 706 707 tok = isl_stream_next_token(s); 708 if (!tok) 709 return 0; 710 if (tok->type == type) { 711 isl_token_free(tok); 712 return 1; 713 } 714 isl_stream_push_token(s, tok); 715 return 0; 716 } 717 718 int isl_stream_next_token_is(__isl_keep isl_stream *s, int type) 719 { 720 struct isl_token *tok; 721 int r; 722 723 tok = isl_stream_next_token(s); 724 if (!tok) 725 return 0; 726 r = tok->type == type; 727 isl_stream_push_token(s, tok); 728 return r; 729 } 730 731 char *isl_stream_read_ident_if_available(__isl_keep isl_stream *s) 732 { 733 struct isl_token *tok; 734 735 tok = isl_stream_next_token(s); 736 if (!tok) 737 return NULL; 738 if (tok->type == ISL_TOKEN_IDENT) { 739 char *ident = strdup(tok->u.s); 740 isl_token_free(tok); 741 return ident; 742 } 743 isl_stream_push_token(s, tok); 744 return NULL; 745 } 746 747 int isl_stream_eat(__isl_keep isl_stream *s, int type) 748 { 749 struct isl_token *tok; 750 751 tok = isl_stream_next_token(s); 752 if (!tok) { 753 if (s->eof) 754 isl_stream_error(s, NULL, "unexpected EOF"); 755 return -1; 756 } 757 if (tok->type == type) { 758 isl_token_free(tok); 759 return 0; 760 } 761 isl_stream_error(s, tok, "expecting other token"); 762 isl_token_free(tok); 763 return -1; 764 } 765 766 int isl_stream_is_empty(__isl_keep isl_stream *s) 767 { 768 struct isl_token *tok; 769 770 tok = isl_stream_next_token(s); 771 772 if (!tok) 773 return 1; 774 775 isl_stream_push_token(s, tok); 776 return 0; 777 } 778 779 static isl_stat free_keyword(void **p, void *user) 780 { 781 struct isl_keyword *keyword = *p; 782 783 free(keyword->name); 784 free(keyword); 785 786 return isl_stat_ok; 787 } 788 789 void isl_stream_flush_tokens(__isl_keep isl_stream *s) 790 { 791 int i; 792 793 if (!s) 794 return; 795 for (i = 0; i < s->n_token; ++i) 796 isl_token_free(s->tokens[i]); 797 s->n_token = 0; 798 } 799 800 isl_ctx *isl_stream_get_ctx(__isl_keep isl_stream *s) 801 { 802 return s ? s->ctx : NULL; 803 } 804 805 void isl_stream_free(__isl_take isl_stream *s) 806 { 807 if (!s) 808 return; 809 free(s->buffer); 810 if (s->n_token != 0) { 811 struct isl_token *tok = isl_stream_next_token(s); 812 isl_stream_error(s, tok, "unexpected token"); 813 isl_token_free(tok); 814 } 815 if (s->keywords) { 816 isl_hash_table_foreach(s->ctx, s->keywords, &free_keyword, NULL); 817 isl_hash_table_free(s->ctx, s->keywords); 818 } 819 free(s->yaml_state); 820 free(s->yaml_indent); 821 isl_ctx_deref(s->ctx); 822 free(s); 823 } 824 825 /* Push "state" onto the stack of currently active YAML elements. 826 * The caller is responsible for setting the corresponding indentation. 827 * Return 0 on success and -1 on failure. 828 */ 829 static int push_state(__isl_keep isl_stream *s, enum isl_yaml_state state) 830 { 831 if (s->yaml_size < s->yaml_depth + 1) { 832 int *indent; 833 enum isl_yaml_state *state; 834 835 state = isl_realloc_array(s->ctx, s->yaml_state, 836 enum isl_yaml_state, s->yaml_depth + 1); 837 if (!state) 838 return -1; 839 s->yaml_state = state; 840 841 indent = isl_realloc_array(s->ctx, s->yaml_indent, 842 int, s->yaml_depth + 1); 843 if (!indent) 844 return -1; 845 s->yaml_indent = indent; 846 847 s->yaml_size = s->yaml_depth + 1; 848 } 849 850 s->yaml_state[s->yaml_depth] = state; 851 s->yaml_depth++; 852 853 return 0; 854 } 855 856 /* Remove the innermost active YAML element from the stack. 857 * Return isl_stat_ok on success and isl_stat_error on failure. 858 */ 859 static isl_stat pop_state(__isl_keep isl_stream *s) 860 { 861 if (!s) 862 return isl_stat_error; 863 if (s->yaml_depth < 1) 864 isl_die(isl_stream_get_ctx(s), isl_error_invalid, 865 "not in YAML construct", return isl_stat_error); 866 867 s->yaml_depth--; 868 869 return isl_stat_ok; 870 } 871 872 /* Set the state of the innermost active YAML element to "state". 873 * Return 0 on success and -1 on failure. 874 */ 875 static int update_state(__isl_keep isl_stream *s, enum isl_yaml_state state) 876 { 877 if (!s) 878 return -1; 879 if (s->yaml_depth < 1) 880 isl_die(isl_stream_get_ctx(s), isl_error_invalid, 881 "not in YAML construct", return -1); 882 883 s->yaml_state[s->yaml_depth - 1] = state; 884 885 return 0; 886 } 887 888 /* Return the state of the innermost active YAML element. 889 * Return isl_yaml_none if we are not inside any YAML element. 890 */ 891 static enum isl_yaml_state current_state(__isl_keep isl_stream *s) 892 { 893 if (!s) 894 return isl_yaml_none; 895 if (s->yaml_depth < 1) 896 return isl_yaml_none; 897 return s->yaml_state[s->yaml_depth - 1]; 898 } 899 900 /* Set the indentation of the innermost active YAML element to "indent". 901 * If "indent" is equal to ISL_YAML_INDENT_FLOW, then this means 902 * that the current element is in flow format. 903 */ 904 static isl_stat set_yaml_indent(__isl_keep isl_stream *s, int indent) 905 { 906 if (s->yaml_depth < 1) 907 isl_die(s->ctx, isl_error_internal, 908 "not in YAML element", return isl_stat_error); 909 910 s->yaml_indent[s->yaml_depth - 1] = indent; 911 912 return isl_stat_ok; 913 } 914 915 /* Return the indentation of the innermost active YAML element 916 * of -1 on error. 917 */ 918 static int get_yaml_indent(__isl_keep isl_stream *s) 919 { 920 if (s->yaml_depth < 1) 921 isl_die(s->ctx, isl_error_internal, 922 "not in YAML element", return -1); 923 924 return s->yaml_indent[s->yaml_depth - 1]; 925 } 926 927 /* Move to the next state at the innermost level. 928 * Return isl_bool_true if successful. 929 * Return isl_bool_false if we are at the end of the innermost level. 930 * Return isl_bool_error on error. 931 * 932 * If we are in state isl_yaml_mapping_key_start, then we have just 933 * started a mapping and we are expecting a key. If the mapping started 934 * with a '{', then we check if the next token is a '}'. If so, 935 * then the mapping is empty and there is no next state at this level. 936 * Otherwise, we assume that there is at least one key (the one from 937 * which we derived the indentation in isl_stream_yaml_read_start_mapping. 938 * 939 * If we are in state isl_yaml_mapping_key, then the we expect a colon 940 * followed by a value, so there is always a next state unless 941 * some error occurs. 942 * 943 * If we are in state isl_yaml_mapping_val, then there may or may 944 * not be a subsequent key in the same mapping. 945 * In flow format, the next key is preceded by a comma. 946 * In block format, the next key has the same indentation as the first key. 947 * If the first token has a smaller indentation, then we have reached 948 * the end of the current mapping. 949 * 950 * If we are in state isl_yaml_sequence_start, then we have just 951 * started a sequence. If the sequence started with a '[', 952 * then we check if the next token is a ']'. If so, then the sequence 953 * is empty and there is no next state at this level. 954 * Otherwise, we assume that there is at least one element in the sequence 955 * (the one from which we derived the indentation in 956 * isl_stream_yaml_read_start_sequence. 957 * 958 * If we are in state isl_yaml_sequence, then there may or may 959 * not be a subsequent element in the same sequence. 960 * In flow format, the next element is preceded by a comma. 961 * In block format, the next element is introduced by a dash with 962 * the same indentation as that of the first element. 963 * If the first token is not a dash or if it has a smaller indentation, 964 * then we have reached the end of the current sequence. 965 */ 966 isl_bool isl_stream_yaml_next(__isl_keep isl_stream *s) 967 { 968 struct isl_token *tok; 969 enum isl_yaml_state state; 970 int indent; 971 972 state = current_state(s); 973 if (state == isl_yaml_none) 974 isl_die(s->ctx, isl_error_invalid, 975 "not in YAML element", return isl_bool_error); 976 switch (state) { 977 case isl_yaml_mapping_key_start: 978 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW && 979 isl_stream_next_token_is(s, '}')) 980 return isl_bool_false; 981 if (update_state(s, isl_yaml_mapping_key) < 0) 982 return isl_bool_error; 983 return isl_bool_true; 984 case isl_yaml_mapping_key: 985 tok = isl_stream_next_token(s); 986 if (!tok) { 987 if (s->eof) 988 isl_stream_error(s, NULL, "unexpected EOF"); 989 return isl_bool_error; 990 } 991 if (tok->type == ':') { 992 isl_token_free(tok); 993 if (update_state(s, isl_yaml_mapping_val) < 0) 994 return isl_bool_error; 995 return isl_bool_true; 996 } 997 isl_stream_error(s, tok, "expecting ':'"); 998 isl_stream_push_token(s, tok); 999 return isl_bool_error; 1000 case isl_yaml_mapping_val: 1001 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) { 1002 if (!isl_stream_eat_if_available(s, ',')) 1003 return isl_bool_false; 1004 if (update_state(s, isl_yaml_mapping_key) < 0) 1005 return isl_bool_error; 1006 return isl_bool_true; 1007 } 1008 tok = isl_stream_next_token(s); 1009 if (!tok) 1010 return isl_bool_false; 1011 indent = tok->col - 1; 1012 isl_stream_push_token(s, tok); 1013 if (indent < get_yaml_indent(s)) 1014 return isl_bool_false; 1015 if (update_state(s, isl_yaml_mapping_key) < 0) 1016 return isl_bool_error; 1017 return isl_bool_true; 1018 case isl_yaml_sequence_start: 1019 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) { 1020 if (isl_stream_next_token_is(s, ']')) 1021 return isl_bool_false; 1022 if (update_state(s, isl_yaml_sequence) < 0) 1023 return isl_bool_error; 1024 return isl_bool_true; 1025 } 1026 tok = isl_stream_next_token(s); 1027 if (!tok) { 1028 if (s->eof) 1029 isl_stream_error(s, NULL, "unexpected EOF"); 1030 return isl_bool_error; 1031 } 1032 if (tok->type == '-') { 1033 isl_token_free(tok); 1034 if (update_state(s, isl_yaml_sequence) < 0) 1035 return isl_bool_error; 1036 return isl_bool_true; 1037 } 1038 isl_stream_error(s, tok, "expecting '-'"); 1039 isl_stream_push_token(s, tok); 1040 return isl_bool_false; 1041 case isl_yaml_sequence: 1042 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) 1043 return isl_bool_ok(isl_stream_eat_if_available(s, ',')); 1044 tok = isl_stream_next_token(s); 1045 if (!tok) 1046 return isl_bool_false; 1047 indent = tok->col - 1; 1048 if (indent < get_yaml_indent(s) || tok->type != '-') { 1049 isl_stream_push_token(s, tok); 1050 return isl_bool_false; 1051 } 1052 isl_token_free(tok); 1053 return isl_bool_true; 1054 default: 1055 isl_die(s->ctx, isl_error_internal, 1056 "unexpected state", return isl_bool_error); 1057 } 1058 } 1059 1060 /* Start reading a YAML mapping. 1061 * Return isl_stat_ok on success and isl_stat_error on error. 1062 * 1063 * If the first token on the stream is a '{' then we remove this token 1064 * from the stream and keep track of the fact that the mapping 1065 * is given in flow format. 1066 * Otherwise, we assume the first token is the first key of the mapping and 1067 * keep track of its indentation, but keep the token on the stream. 1068 * In both cases, the next token we expect is the first key of the mapping. 1069 */ 1070 isl_stat isl_stream_yaml_read_start_mapping(__isl_keep isl_stream *s) 1071 { 1072 struct isl_token *tok; 1073 int indent; 1074 1075 if (push_state(s, isl_yaml_mapping_key_start) < 0) 1076 return isl_stat_error; 1077 1078 tok = isl_stream_next_token(s); 1079 if (!tok) { 1080 if (s->eof) 1081 isl_stream_error(s, NULL, "unexpected EOF"); 1082 return isl_stat_error; 1083 } 1084 if (isl_token_get_type(tok) == '{') { 1085 isl_token_free(tok); 1086 return set_yaml_indent(s, ISL_YAML_INDENT_FLOW); 1087 } 1088 indent = tok->col - 1; 1089 isl_stream_push_token(s, tok); 1090 1091 return set_yaml_indent(s, indent); 1092 } 1093 1094 /* Finish reading a YAML mapping. 1095 * Return isl_stat_ok on success and isl_stat_error on error. 1096 * 1097 * If the mapping started with a '{', then we expect a '}' to close 1098 * the mapping. 1099 * Otherwise, we double-check that the next token (if any) 1100 * has a smaller indentation than that of the current mapping. 1101 */ 1102 isl_stat isl_stream_yaml_read_end_mapping(__isl_keep isl_stream *s) 1103 { 1104 struct isl_token *tok; 1105 int indent; 1106 1107 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) { 1108 if (isl_stream_eat(s, '}') < 0) 1109 return isl_stat_error; 1110 return pop_state(s); 1111 } 1112 1113 tok = isl_stream_next_token(s); 1114 if (!tok) 1115 return pop_state(s); 1116 1117 indent = tok->col - 1; 1118 isl_stream_push_token(s, tok); 1119 1120 if (indent >= get_yaml_indent(s)) 1121 isl_die(isl_stream_get_ctx(s), isl_error_invalid, 1122 "mapping not finished", return isl_stat_error); 1123 1124 return pop_state(s); 1125 } 1126 1127 /* Start reading a YAML sequence. 1128 * Return isl_stat_ok on success and isl_stat_error on error. 1129 * 1130 * If the first token on the stream is a '[' then we remove this token 1131 * from the stream and keep track of the fact that the sequence 1132 * is given in flow format. 1133 * Otherwise, we assume the first token is the dash that introduces 1134 * the first element of the sequence and keep track of its indentation, 1135 * but keep the token on the stream. 1136 * In both cases, the next token we expect is the first element 1137 * of the sequence. 1138 */ 1139 isl_stat isl_stream_yaml_read_start_sequence(__isl_keep isl_stream *s) 1140 { 1141 struct isl_token *tok; 1142 int indent; 1143 1144 if (push_state(s, isl_yaml_sequence_start) < 0) 1145 return isl_stat_error; 1146 1147 tok = isl_stream_next_token(s); 1148 if (!tok) { 1149 if (s->eof) 1150 isl_stream_error(s, NULL, "unexpected EOF"); 1151 return isl_stat_error; 1152 } 1153 if (isl_token_get_type(tok) == '[') { 1154 isl_token_free(tok); 1155 return set_yaml_indent(s, ISL_YAML_INDENT_FLOW); 1156 } 1157 indent = tok->col - 1; 1158 isl_stream_push_token(s, tok); 1159 1160 return set_yaml_indent(s, indent); 1161 } 1162 1163 /* Finish reading a YAML sequence. 1164 * Return isl_stat_ok on success and isl_stat_error on error. 1165 * 1166 * If the sequence started with a '[', then we expect a ']' to close 1167 * the sequence. 1168 * Otherwise, we double-check that the next token (if any) 1169 * is not a dash or that it has a smaller indentation than 1170 * that of the current sequence. 1171 */ 1172 isl_stat isl_stream_yaml_read_end_sequence(__isl_keep isl_stream *s) 1173 { 1174 struct isl_token *tok; 1175 int indent; 1176 int dash; 1177 1178 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) { 1179 if (isl_stream_eat(s, ']') < 0) 1180 return isl_stat_error; 1181 return pop_state(s); 1182 } 1183 1184 tok = isl_stream_next_token(s); 1185 if (!tok) 1186 return pop_state(s); 1187 1188 indent = tok->col - 1; 1189 dash = tok->type == '-'; 1190 isl_stream_push_token(s, tok); 1191 1192 if (indent >= get_yaml_indent(s) && dash) 1193 isl_die(isl_stream_get_ctx(s), isl_error_invalid, 1194 "sequence not finished", return isl_stat_error); 1195 1196 return pop_state(s); 1197 } 1198