1198 lines · c
1/*2 * Copyright 2008-2009 Katholieke Universiteit Leuven3 *4 * Use of this software is governed by the MIT license5 *6 * Written by Sven Verdoolaege, K.U.Leuven, Departement7 * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium8 */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 19struct isl_keyword {20 char *name;21 enum isl_token_type type;22};23 24static 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 31enum 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 70struct 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 */86int 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 */106isl_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 127void 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 else138 free(tok->u.s);139 free(tok);140}141 142void 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 else182 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 188static __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;214error: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->col241 * to point to the next character.242 */243static 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 } else263 s->col++;264 s->c = c;265 return c;266}267 268static 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 column277 * of the returned character.278 */279static 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 297static 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 311void 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 317static 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 376int 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 387static 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 } else619 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 } else631 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;688error:689 isl_token_free(tok);690 return NULL;691}692 693struct isl_token *isl_stream_next_token(__isl_keep isl_stream *s)694{695 return next_token(s, 0);696}697 698struct isl_token *isl_stream_next_token_on_same_line(__isl_keep isl_stream *s)699{700 return next_token(s, 1);701}702 703int 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 718int 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 731char *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 747int 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 766int 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 779static 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 789void 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 800isl_ctx *isl_stream_get_ctx(__isl_keep isl_stream *s)801{802 return s ? s->ctx : NULL;803}804 805void 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 */829static 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 */859static 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 */875static 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 */891static 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 means902 * that the current element is in flow format.903 */904static 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 element916 * of -1 on error.917 */918static 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 just933 * started a mapping and we are expecting a key. If the mapping started934 * 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 from937 * 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 colon940 * followed by a value, so there is always a next state unless941 * some error occurs.942 *943 * If we are in state isl_yaml_mapping_val, then there may or may944 * 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 reached948 * the end of the current mapping.949 *950 * If we are in state isl_yaml_sequence_start, then we have just951 * started a sequence. If the sequence started with a '[',952 * then we check if the next token is a ']'. If so, then the sequence953 * is empty and there is no next state at this level.954 * Otherwise, we assume that there is at least one element in the sequence955 * (the one from which we derived the indentation in956 * isl_stream_yaml_read_start_sequence.957 *958 * If we are in state isl_yaml_sequence, then there may or may959 * 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 with962 * 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 */966isl_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 token1064 * from the stream and keep track of the fact that the mapping1065 * is given in flow format.1066 * Otherwise, we assume the first token is the first key of the mapping and1067 * 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 */1070isl_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 close1098 * 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 */1102isl_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 token1131 * from the stream and keep track of the fact that the sequence1132 * is given in flow format.1133 * Otherwise, we assume the first token is the dash that introduces1134 * 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 element1137 * of the sequence.1138 */1139isl_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 close1167 * 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 than1170 * that of the current sequence.1171 */1172isl_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