isl_scheduler.c: rename extract_sccs to isl_sched_graph_extract_sccs
[isl.git] / isl_stream.c
blob1052360aa326af851123074fc6ba227e1024b94b
1 /*
2 * Copyright 2008-2009 Katholieke Universiteit Leuven
4 * Use of this software is governed by the MIT license
6 * Written by Sven Verdoolaege, K.U.Leuven, Departement
7 * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
8 */
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>
19 struct isl_keyword {
20 char *name;
21 enum isl_token_type type;
24 static isl_bool same_name(const void *entry, const void *val)
26 const struct isl_keyword *keyword = (const struct isl_keyword *)entry;
28 return isl_bool_ok(!strcmp(keyword->name, val));
31 enum isl_token_type isl_stream_register_keyword(__isl_keep isl_stream *s,
32 const char *name)
34 struct isl_hash_table_entry *entry;
35 struct isl_keyword *keyword;
36 uint32_t name_hash;
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;
45 name_hash = isl_hash_string(isl_hash_init(), name);
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;
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;
65 entry->data = keyword;
67 return keyword->type;
70 struct isl_token *isl_token_new(isl_ctx *ctx,
71 int line, int col, unsigned on_new_line)
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;
84 /* Return the type of "tok".
86 int isl_token_get_type(struct isl_token *tok)
88 return tok ? tok->type : ISL_TOKEN_ERROR;
91 /* Given a token of type ISL_TOKEN_VALUE, return the value it represents.
93 __isl_give isl_val *isl_token_get_val(isl_ctx *ctx, struct isl_token *tok)
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);
101 return isl_val_int_from_isl_int(ctx, tok->u.v);
104 /* Given a token with a string representation, return a copy of this string.
106 __isl_give char *isl_token_get_str(isl_ctx *ctx, struct isl_token *tok)
108 if (!tok)
109 return NULL;
110 if (!tok->u.s)
111 isl_die(ctx, isl_error_invalid,
112 "token does not have a string representation",
113 return NULL);
115 return strdup(tok->u.s);
118 void isl_token_free(struct isl_token *tok)
120 if (!tok)
121 return;
122 if (tok->type == ISL_TOKEN_VALUE)
123 isl_int_clear(tok->u.v);
124 else if (tok->type == ISL_TOKEN_MAP)
125 isl_map_free(tok->u.map);
126 else if (tok->type == ISL_TOKEN_AFF)
127 isl_pw_aff_free(tok->u.pwaff);
128 else
129 free(tok->u.s);
130 free(tok);
133 void isl_stream_error(__isl_keep isl_stream *s, struct isl_token *tok,
134 char *msg)
136 int line = tok ? tok->line : s->line;
137 int col = tok ? tok->col : s->col;
139 isl_ctx_set_full_error(s->ctx, isl_error_invalid, "syntax error",
140 __FILE__, __LINE__);
142 if (s->ctx->opt->on_error == ISL_ON_ERROR_CONTINUE)
143 return;
144 fprintf(stderr, "syntax error (%d, %d): %s\n", line, col, msg);
145 if (tok) {
146 if (tok->type < 256)
147 fprintf(stderr, "got '%c'\n", tok->type);
148 else if (tok->type == ISL_TOKEN_IDENT)
149 fprintf(stderr, "got ident '%s'\n", tok->u.s);
150 else if (tok->is_keyword)
151 fprintf(stderr, "got keyword '%s'\n", tok->u.s);
152 else if (tok->type == ISL_TOKEN_VALUE) {
153 fprintf(stderr, "got value '");
154 isl_int_print(stderr, tok->u.v, 0);
155 fprintf(stderr, "'\n");
156 } else if (tok->type == ISL_TOKEN_MAP) {
157 isl_printer *p;
158 fprintf(stderr, "got map '");
159 p = isl_printer_to_file(s->ctx, stderr);
160 p = isl_printer_print_map(p, tok->u.map);
161 isl_printer_free(p);
162 fprintf(stderr, "'\n");
163 } else if (tok->type == ISL_TOKEN_AFF) {
164 isl_printer *p;
165 fprintf(stderr, "got affine expression '");
166 p = isl_printer_to_file(s->ctx, stderr);
167 p = isl_printer_print_pw_aff(p, tok->u.pwaff);
168 isl_printer_free(p);
169 fprintf(stderr, "'\n");
170 } else if (tok->u.s)
171 fprintf(stderr, "got token '%s'\n", tok->u.s);
172 else
173 fprintf(stderr, "got token type %d\n", tok->type);
175 if (s->ctx->opt->on_error == ISL_ON_ERROR_ABORT)
176 abort();
179 static __isl_give isl_stream* isl_stream_new(struct isl_ctx *ctx)
181 int i;
182 isl_stream *s = isl_calloc_type(ctx, struct isl_stream);
183 if (!s)
184 return NULL;
185 s->ctx = ctx;
186 isl_ctx_ref(s->ctx);
187 s->file = NULL;
188 s->str = NULL;
189 s->len = 0;
190 s->line = 1;
191 s->col = 1;
192 s->eof = 0;
193 s->last_line = 0;
194 s->c = -1;
195 s->n_un = 0;
196 for (i = 0; i < 5; ++i)
197 s->tokens[i] = NULL;
198 s->n_token = 0;
199 s->keywords = NULL;
200 s->size = 256;
201 s->buffer = isl_alloc_array(ctx, char, s->size);
202 if (!s->buffer)
203 goto error;
204 return s;
205 error:
206 isl_stream_free(s);
207 return NULL;
210 __isl_give isl_stream* isl_stream_new_file(struct isl_ctx *ctx, FILE *file)
212 isl_stream *s = isl_stream_new(ctx);
213 if (!s)
214 return NULL;
215 s->file = file;
216 return s;
219 __isl_give isl_stream* isl_stream_new_str(struct isl_ctx *ctx, const char *str)
221 isl_stream *s;
222 if (!str)
223 return NULL;
224 s = isl_stream_new(ctx);
225 if (!s)
226 return NULL;
227 s->str = str;
228 return s;
231 /* Read a character from the stream and advance s->line and s->col
232 * to point to the next character.
234 static int stream_getc(__isl_keep isl_stream *s)
236 int c;
237 if (s->eof)
238 return -1;
239 if (s->n_un)
240 return s->c = s->un[--s->n_un];
241 if (s->file)
242 c = fgetc(s->file);
243 else {
244 c = *s->str++;
245 if (c == '\0')
246 c = -1;
248 if (c == -1)
249 s->eof = 1;
250 else if (c == '\n') {
251 s->line++;
252 s->col = 1;
253 } else
254 s->col++;
255 s->c = c;
256 return c;
259 static void isl_stream_ungetc(__isl_keep isl_stream *s, int c)
261 isl_assert(s->ctx, s->n_un < 5, return);
262 s->un[s->n_un++] = c;
263 s->c = -1;
266 /* Read a character from the stream, skipping pairs of '\\' and '\n'.
267 * Set s->start_line and s->start_col to the line and column
268 * of the returned character.
270 static int isl_stream_getc(__isl_keep isl_stream *s)
272 int c;
274 do {
275 s->start_line = s->line;
276 s->start_col = s->col;
277 c = stream_getc(s);
278 if (c != '\\')
279 return c;
280 c = stream_getc(s);
281 } while (c == '\n');
283 isl_stream_ungetc(s, c);
285 return '\\';
288 static int isl_stream_push_char(__isl_keep isl_stream *s, int c)
290 if (s->len >= s->size) {
291 char *buffer;
292 s->size = (3*s->size)/2;
293 buffer = isl_realloc_array(s->ctx, s->buffer, char, s->size);
294 if (!buffer)
295 return -1;
296 s->buffer = buffer;
298 s->buffer[s->len++] = c;
299 return 0;
302 void isl_stream_push_token(__isl_keep isl_stream *s, struct isl_token *tok)
304 isl_assert(s->ctx, s->n_token < 5, return);
305 s->tokens[s->n_token++] = tok;
308 static enum isl_token_type check_keywords(__isl_keep isl_stream *s)
310 struct isl_hash_table_entry *entry;
311 struct isl_keyword *keyword;
312 uint32_t name_hash;
314 if (!strcasecmp(s->buffer, "exists"))
315 return ISL_TOKEN_EXISTS;
316 if (!strcasecmp(s->buffer, "and"))
317 return ISL_TOKEN_AND;
318 if (!strcasecmp(s->buffer, "or"))
319 return ISL_TOKEN_OR;
320 if (!strcasecmp(s->buffer, "implies"))
321 return ISL_TOKEN_IMPLIES;
322 if (!strcasecmp(s->buffer, "not"))
323 return ISL_TOKEN_NOT;
324 if (!strcasecmp(s->buffer, "infty"))
325 return ISL_TOKEN_INFTY;
326 if (!strcasecmp(s->buffer, "infinity"))
327 return ISL_TOKEN_INFTY;
328 if (!strcasecmp(s->buffer, "NaN"))
329 return ISL_TOKEN_NAN;
330 if (!strcasecmp(s->buffer, "min"))
331 return ISL_TOKEN_MIN;
332 if (!strcasecmp(s->buffer, "max"))
333 return ISL_TOKEN_MAX;
334 if (!strcasecmp(s->buffer, "rat"))
335 return ISL_TOKEN_RAT;
336 if (!strcasecmp(s->buffer, "true"))
337 return ISL_TOKEN_TRUE;
338 if (!strcasecmp(s->buffer, "false"))
339 return ISL_TOKEN_FALSE;
340 if (!strcasecmp(s->buffer, "ceild"))
341 return ISL_TOKEN_CEILD;
342 if (!strcasecmp(s->buffer, "floord"))
343 return ISL_TOKEN_FLOORD;
344 if (!strcasecmp(s->buffer, "mod"))
345 return ISL_TOKEN_MOD;
346 if (!strcasecmp(s->buffer, "ceil"))
347 return ISL_TOKEN_CEIL;
348 if (!strcasecmp(s->buffer, "floor"))
349 return ISL_TOKEN_FLOOR;
351 if (!s->keywords)
352 return ISL_TOKEN_IDENT;
354 name_hash = isl_hash_string(isl_hash_init(), s->buffer);
355 entry = isl_hash_table_find(s->ctx, s->keywords, name_hash, same_name,
356 s->buffer, 0);
357 if (!entry)
358 return ISL_TOKEN_ERROR;
359 if (entry != isl_hash_table_entry_none) {
360 keyword = entry->data;
361 return keyword->type;
364 return ISL_TOKEN_IDENT;
367 int isl_stream_skip_line(__isl_keep isl_stream *s)
369 int c;
371 while ((c = isl_stream_getc(s)) != -1 && c != '\n')
372 /* nothing */
375 return c == -1 ? -1 : 0;
378 static struct isl_token *next_token(__isl_keep isl_stream *s, int same_line)
380 int c;
381 struct isl_token *tok = NULL;
382 int line, col;
383 int old_line = s->last_line;
385 if (s->n_token) {
386 if (same_line && s->tokens[s->n_token - 1]->on_new_line)
387 return NULL;
388 return s->tokens[--s->n_token];
391 if (same_line && s->c == '\n')
392 return NULL;
394 s->len = 0;
396 /* skip spaces and comment lines */
397 while ((c = isl_stream_getc(s)) != -1) {
398 if (c == '#') {
399 if (isl_stream_skip_line(s) < 0)
400 break;
401 c = '\n';
402 if (same_line)
403 break;
404 } else if (!isspace(c) || (same_line && c == '\n'))
405 break;
408 line = s->start_line;
409 col = s->start_col;
411 if (c == -1 || (same_line && c == '\n'))
412 return NULL;
413 s->last_line = line;
415 if (c == '(' ||
416 c == ')' ||
417 c == '+' ||
418 c == '*' ||
419 c == '%' ||
420 c == '?' ||
421 c == '^' ||
422 c == '@' ||
423 c == '$' ||
424 c == ',' ||
425 c == '.' ||
426 c == ';' ||
427 c == '[' ||
428 c == ']' ||
429 c == '{' ||
430 c == '}') {
431 tok = isl_token_new(s->ctx, line, col, old_line != line);
432 if (!tok)
433 return NULL;
434 tok->type = (enum isl_token_type)c;
435 return tok;
437 if (c == '-') {
438 int c;
439 if ((c = isl_stream_getc(s)) == '>') {
440 tok = isl_token_new(s->ctx, line, col, old_line != line);
441 if (!tok)
442 return NULL;
443 tok->u.s = strdup("->");
444 tok->type = ISL_TOKEN_TO;
445 return tok;
447 if (c != -1)
448 isl_stream_ungetc(s, c);
449 if (!isdigit(c)) {
450 tok = isl_token_new(s->ctx, line, col, old_line != line);
451 if (!tok)
452 return NULL;
453 tok->type = (enum isl_token_type) '-';
454 return tok;
457 if (c == '-' || isdigit(c)) {
458 int minus = c == '-';
459 tok = isl_token_new(s->ctx, line, col, old_line != line);
460 if (!tok)
461 return NULL;
462 tok->type = ISL_TOKEN_VALUE;
463 isl_int_init(tok->u.v);
464 if (isl_stream_push_char(s, c))
465 goto error;
466 while ((c = isl_stream_getc(s)) != -1 && isdigit(c))
467 if (isl_stream_push_char(s, c))
468 goto error;
469 if (c != -1)
470 isl_stream_ungetc(s, c);
471 isl_stream_push_char(s, '\0');
472 isl_int_read(tok->u.v, s->buffer);
473 if (minus && isl_int_is_zero(tok->u.v)) {
474 tok->col++;
475 tok->on_new_line = 0;
476 isl_stream_push_token(s, tok);
477 tok = isl_token_new(s->ctx, line, col, old_line != line);
478 if (!tok)
479 return NULL;
480 tok->type = (enum isl_token_type) '-';
482 return tok;
484 if (isalpha(c) || c == '_') {
485 tok = isl_token_new(s->ctx, line, col, old_line != line);
486 if (!tok)
487 return NULL;
488 isl_stream_push_char(s, c);
489 while ((c = isl_stream_getc(s)) != -1 &&
490 (isalnum(c) || c == '_'))
491 isl_stream_push_char(s, c);
492 if (c != -1)
493 isl_stream_ungetc(s, c);
494 while ((c = isl_stream_getc(s)) != -1 && c == '\'')
495 isl_stream_push_char(s, c);
496 if (c != -1)
497 isl_stream_ungetc(s, c);
498 isl_stream_push_char(s, '\0');
499 tok->type = check_keywords(s);
500 if (tok->type != ISL_TOKEN_IDENT)
501 tok->is_keyword = 1;
502 tok->u.s = strdup(s->buffer);
503 if (!tok->u.s)
504 goto error;
505 return tok;
507 if (c == '"') {
508 tok = isl_token_new(s->ctx, line, col, old_line != line);
509 if (!tok)
510 return NULL;
511 tok->type = ISL_TOKEN_STRING;
512 tok->u.s = NULL;
513 while ((c = isl_stream_getc(s)) != -1 && c != '"' && c != '\n')
514 isl_stream_push_char(s, c);
515 if (c != '"') {
516 isl_stream_error(s, NULL, "unterminated string");
517 goto error;
519 isl_stream_push_char(s, '\0');
520 tok->u.s = strdup(s->buffer);
521 return tok;
523 if (c == '=') {
524 int c;
525 tok = isl_token_new(s->ctx, line, col, old_line != line);
526 if (!tok)
527 return NULL;
528 if ((c = isl_stream_getc(s)) == '=') {
529 tok->u.s = strdup("==");
530 tok->type = ISL_TOKEN_EQ_EQ;
531 return tok;
533 if (c != -1)
534 isl_stream_ungetc(s, c);
535 tok->type = (enum isl_token_type) '=';
536 return tok;
538 if (c == ':') {
539 int c;
540 tok = isl_token_new(s->ctx, line, col, old_line != line);
541 if (!tok)
542 return NULL;
543 if ((c = isl_stream_getc(s)) == '=') {
544 tok->u.s = strdup(":=");
545 tok->type = ISL_TOKEN_DEF;
546 return tok;
548 if (c != -1)
549 isl_stream_ungetc(s, c);
550 tok->type = (enum isl_token_type) ':';
551 return tok;
553 if (c == '>') {
554 int c;
555 tok = isl_token_new(s->ctx, line, col, old_line != line);
556 if (!tok)
557 return NULL;
558 if ((c = isl_stream_getc(s)) == '=') {
559 tok->u.s = strdup(">=");
560 tok->type = ISL_TOKEN_GE;
561 return tok;
562 } else if (c == '>') {
563 if ((c = isl_stream_getc(s)) == '=') {
564 tok->u.s = strdup(">>=");
565 tok->type = ISL_TOKEN_LEX_GE;
566 return tok;
568 tok->u.s = strdup(">>");
569 tok->type = ISL_TOKEN_LEX_GT;
570 } else {
571 tok->u.s = strdup(">");
572 tok->type = ISL_TOKEN_GT;
574 if (c != -1)
575 isl_stream_ungetc(s, c);
576 return tok;
578 if (c == '<') {
579 int c;
580 tok = isl_token_new(s->ctx, line, col, old_line != line);
581 if (!tok)
582 return NULL;
583 if ((c = isl_stream_getc(s)) == '=') {
584 tok->u.s = strdup("<=");
585 tok->type = ISL_TOKEN_LE;
586 return tok;
587 } else if (c == '<') {
588 if ((c = isl_stream_getc(s)) == '=') {
589 tok->u.s = strdup("<<=");
590 tok->type = ISL_TOKEN_LEX_LE;
591 return tok;
593 tok->u.s = strdup("<<");
594 tok->type = ISL_TOKEN_LEX_LT;
595 } else {
596 tok->u.s = strdup("<");
597 tok->type = ISL_TOKEN_LT;
599 if (c != -1)
600 isl_stream_ungetc(s, c);
601 return tok;
603 if (c == '&') {
604 tok = isl_token_new(s->ctx, line, col, old_line != line);
605 if (!tok)
606 return NULL;
607 tok->type = ISL_TOKEN_AND;
608 if ((c = isl_stream_getc(s)) != '&' && c != -1) {
609 tok->u.s = strdup("&");
610 isl_stream_ungetc(s, c);
611 } else
612 tok->u.s = strdup("&&");
613 return tok;
615 if (c == '|') {
616 tok = isl_token_new(s->ctx, line, col, old_line != line);
617 if (!tok)
618 return NULL;
619 tok->type = ISL_TOKEN_OR;
620 if ((c = isl_stream_getc(s)) != '|' && c != -1) {
621 tok->u.s = strdup("|");
622 isl_stream_ungetc(s, c);
623 } else
624 tok->u.s = strdup("||");
625 return tok;
627 if (c == '/') {
628 tok = isl_token_new(s->ctx, line, col, old_line != line);
629 if (!tok)
630 return NULL;
631 if ((c = isl_stream_getc(s)) == '\\') {
632 tok->u.s = strdup("/\\");
633 tok->type = ISL_TOKEN_AND;
634 return tok;
635 } else if (c == '/') {
636 tok->u.s = strdup("//");
637 tok->type = ISL_TOKEN_INT_DIV;
638 return tok;
639 } else {
640 tok->type = (enum isl_token_type) '/';
642 if (c != -1)
643 isl_stream_ungetc(s, c);
644 return tok;
646 if (c == '\\') {
647 tok = isl_token_new(s->ctx, line, col, old_line != line);
648 if (!tok)
649 return NULL;
650 if ((c = isl_stream_getc(s)) != '/' && c != -1) {
651 tok->type = (enum isl_token_type) '\\';
652 isl_stream_ungetc(s, c);
653 } else {
654 tok->u.s = strdup("\\/");
655 tok->type = ISL_TOKEN_OR;
657 return tok;
659 if (c == '!') {
660 tok = isl_token_new(s->ctx, line, col, old_line != line);
661 if (!tok)
662 return NULL;
663 if ((c = isl_stream_getc(s)) == '=') {
664 tok->u.s = strdup("!=");
665 tok->type = ISL_TOKEN_NE;
666 return tok;
667 } else {
668 tok->type = ISL_TOKEN_NOT;
669 tok->u.s = strdup("!");
671 if (c != -1)
672 isl_stream_ungetc(s, c);
673 return tok;
676 tok = isl_token_new(s->ctx, line, col, old_line != line);
677 if (!tok)
678 return NULL;
679 tok->type = ISL_TOKEN_UNKNOWN;
680 return tok;
681 error:
682 isl_token_free(tok);
683 return NULL;
686 struct isl_token *isl_stream_next_token(__isl_keep isl_stream *s)
688 return next_token(s, 0);
691 struct isl_token *isl_stream_next_token_on_same_line(__isl_keep isl_stream *s)
693 return next_token(s, 1);
696 int isl_stream_eat_if_available(__isl_keep isl_stream *s, int type)
698 struct isl_token *tok;
700 tok = isl_stream_next_token(s);
701 if (!tok)
702 return 0;
703 if (tok->type == type) {
704 isl_token_free(tok);
705 return 1;
707 isl_stream_push_token(s, tok);
708 return 0;
711 int isl_stream_next_token_is(__isl_keep isl_stream *s, int type)
713 struct isl_token *tok;
714 int r;
716 tok = isl_stream_next_token(s);
717 if (!tok)
718 return 0;
719 r = tok->type == type;
720 isl_stream_push_token(s, tok);
721 return r;
724 char *isl_stream_read_ident_if_available(__isl_keep isl_stream *s)
726 struct isl_token *tok;
728 tok = isl_stream_next_token(s);
729 if (!tok)
730 return NULL;
731 if (tok->type == ISL_TOKEN_IDENT) {
732 char *ident = strdup(tok->u.s);
733 isl_token_free(tok);
734 return ident;
736 isl_stream_push_token(s, tok);
737 return NULL;
740 int isl_stream_eat(__isl_keep isl_stream *s, int type)
742 struct isl_token *tok;
744 tok = isl_stream_next_token(s);
745 if (!tok) {
746 if (s->eof)
747 isl_stream_error(s, NULL, "unexpected EOF");
748 return -1;
750 if (tok->type == type) {
751 isl_token_free(tok);
752 return 0;
754 isl_stream_error(s, tok, "expecting other token");
755 isl_stream_push_token(s, tok);
756 return -1;
759 int isl_stream_is_empty(__isl_keep isl_stream *s)
761 struct isl_token *tok;
763 tok = isl_stream_next_token(s);
765 if (!tok)
766 return 1;
768 isl_stream_push_token(s, tok);
769 return 0;
772 static isl_stat free_keyword(void **p, void *user)
774 struct isl_keyword *keyword = *p;
776 free(keyword->name);
777 free(keyword);
779 return isl_stat_ok;
782 void isl_stream_flush_tokens(__isl_keep isl_stream *s)
784 int i;
786 if (!s)
787 return;
788 for (i = 0; i < s->n_token; ++i)
789 isl_token_free(s->tokens[i]);
790 s->n_token = 0;
793 isl_ctx *isl_stream_get_ctx(__isl_keep isl_stream *s)
795 return s ? s->ctx : NULL;
798 void isl_stream_free(__isl_take isl_stream *s)
800 if (!s)
801 return;
802 free(s->buffer);
803 if (s->n_token != 0) {
804 struct isl_token *tok = isl_stream_next_token(s);
805 isl_stream_error(s, tok, "unexpected token");
806 isl_token_free(tok);
808 if (s->keywords) {
809 isl_hash_table_foreach(s->ctx, s->keywords, &free_keyword, NULL);
810 isl_hash_table_free(s->ctx, s->keywords);
812 free(s->yaml_state);
813 free(s->yaml_indent);
814 isl_ctx_deref(s->ctx);
815 free(s);
818 /* Push "state" onto the stack of currently active YAML elements.
819 * The caller is responsible for setting the corresponding indentation.
820 * Return 0 on success and -1 on failure.
822 static int push_state(__isl_keep isl_stream *s, enum isl_yaml_state state)
824 if (s->yaml_size < s->yaml_depth + 1) {
825 int *indent;
826 enum isl_yaml_state *state;
828 state = isl_realloc_array(s->ctx, s->yaml_state,
829 enum isl_yaml_state, s->yaml_depth + 1);
830 if (!state)
831 return -1;
832 s->yaml_state = state;
834 indent = isl_realloc_array(s->ctx, s->yaml_indent,
835 int, s->yaml_depth + 1);
836 if (!indent)
837 return -1;
838 s->yaml_indent = indent;
840 s->yaml_size = s->yaml_depth + 1;
843 s->yaml_state[s->yaml_depth] = state;
844 s->yaml_depth++;
846 return 0;
849 /* Remove the innermost active YAML element from the stack.
850 * Return 0 on success and -1 on failure.
852 static int pop_state(__isl_keep isl_stream *s)
854 if (!s)
855 return -1;
856 if (s->yaml_depth < 1)
857 isl_die(isl_stream_get_ctx(s), isl_error_invalid,
858 "not in YAML construct", return -1);
860 s->yaml_depth--;
862 return 0;
865 /* Set the state of the innermost active YAML element to "state".
866 * Return 0 on success and -1 on failure.
868 static int update_state(__isl_keep isl_stream *s, enum isl_yaml_state state)
870 if (!s)
871 return -1;
872 if (s->yaml_depth < 1)
873 isl_die(isl_stream_get_ctx(s), isl_error_invalid,
874 "not in YAML construct", return -1);
876 s->yaml_state[s->yaml_depth - 1] = state;
878 return 0;
881 /* Return the state of the innermost active YAML element.
882 * Return isl_yaml_none if we are not inside any YAML element.
884 static enum isl_yaml_state current_state(__isl_keep isl_stream *s)
886 if (!s)
887 return isl_yaml_none;
888 if (s->yaml_depth < 1)
889 return isl_yaml_none;
890 return s->yaml_state[s->yaml_depth - 1];
893 /* Set the indentation of the innermost active YAML element to "indent".
894 * If "indent" is equal to ISL_YAML_INDENT_FLOW, then this means
895 * that the current elemient is in flow format.
897 static int set_yaml_indent(__isl_keep isl_stream *s, int indent)
899 if (s->yaml_depth < 1)
900 isl_die(s->ctx, isl_error_internal,
901 "not in YAML element", return -1);
903 s->yaml_indent[s->yaml_depth - 1] = indent;
905 return 0;
908 /* Return the indentation of the innermost active YAML element
909 * of -1 on error.
911 static int get_yaml_indent(__isl_keep isl_stream *s)
913 if (s->yaml_depth < 1)
914 isl_die(s->ctx, isl_error_internal,
915 "not in YAML element", return -1);
917 return s->yaml_indent[s->yaml_depth - 1];
920 /* Move to the next state at the innermost level.
921 * Return 1 if successful.
922 * Return 0 if we are at the end of the innermost level.
923 * Return -1 on error.
925 * If we are in state isl_yaml_mapping_key_start, then we have just
926 * started a mapping and we are expecting a key. If the mapping started
927 * with a '{', then we check if the next token is a '}'. If so,
928 * then the mapping is empty and there is no next state at this level.
929 * Otherwise, we assume that there is at least one key (the one from
930 * which we derived the indentation in isl_stream_yaml_read_start_mapping.
932 * If we are in state isl_yaml_mapping_key, then the we expect a colon
933 * followed by a value, so there is always a next state unless
934 * some error occurs.
936 * If we are in state isl_yaml_mapping_val, then there may or may
937 * not be a subsequent key in the same mapping.
938 * In flow format, the next key is preceded by a comma.
939 * In block format, the next key has the same indentation as the first key.
940 * If the first token has a smaller indentation, then we have reached
941 * the end of the current mapping.
943 * If we are in state isl_yaml_sequence_start, then we have just
944 * started a sequence. If the sequence started with a '[',
945 * then we check if the next token is a ']'. If so, then the sequence
946 * is empty and there is no next state at this level.
947 * Otherwise, we assume that there is at least one element in the sequence
948 * (the one from which we derived the indentation in
949 * isl_stream_yaml_read_start_sequence.
951 * If we are in state isl_yaml_sequence, then there may or may
952 * not be a subsequent element in the same sequence.
953 * In flow format, the next element is preceded by a comma.
954 * In block format, the next element is introduced by a dash with
955 * the same indentation as that of the first element.
956 * If the first token is not a dash or if it has a smaller indentation,
957 * then we have reached the end of the current sequence.
959 int isl_stream_yaml_next(__isl_keep isl_stream *s)
961 struct isl_token *tok;
962 enum isl_yaml_state state;
963 int indent;
965 state = current_state(s);
966 if (state == isl_yaml_none)
967 isl_die(s->ctx, isl_error_invalid,
968 "not in YAML element", return -1);
969 switch (state) {
970 case isl_yaml_mapping_key_start:
971 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW &&
972 isl_stream_next_token_is(s, '}'))
973 return 0;
974 if (update_state(s, isl_yaml_mapping_key) < 0)
975 return -1;
976 return 1;
977 case isl_yaml_mapping_key:
978 tok = isl_stream_next_token(s);
979 if (!tok) {
980 if (s->eof)
981 isl_stream_error(s, NULL, "unexpected EOF");
982 return -1;
984 if (tok->type == ':') {
985 isl_token_free(tok);
986 if (update_state(s, isl_yaml_mapping_val) < 0)
987 return -1;
988 return 1;
990 isl_stream_error(s, tok, "expecting ':'");
991 isl_stream_push_token(s, tok);
992 return -1;
993 case isl_yaml_mapping_val:
994 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) {
995 if (!isl_stream_eat_if_available(s, ','))
996 return 0;
997 if (update_state(s, isl_yaml_mapping_key) < 0)
998 return -1;
999 return 1;
1001 tok = isl_stream_next_token(s);
1002 if (!tok)
1003 return 0;
1004 indent = tok->col - 1;
1005 isl_stream_push_token(s, tok);
1006 if (indent < get_yaml_indent(s))
1007 return 0;
1008 if (update_state(s, isl_yaml_mapping_key) < 0)
1009 return -1;
1010 return 1;
1011 case isl_yaml_sequence_start:
1012 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) {
1013 if (isl_stream_next_token_is(s, ']'))
1014 return 0;
1015 if (update_state(s, isl_yaml_sequence) < 0)
1016 return -1;
1017 return 1;
1019 tok = isl_stream_next_token(s);
1020 if (!tok) {
1021 if (s->eof)
1022 isl_stream_error(s, NULL, "unexpected EOF");
1023 return -1;
1025 if (tok->type == '-') {
1026 isl_token_free(tok);
1027 if (update_state(s, isl_yaml_sequence) < 0)
1028 return -1;
1029 return 1;
1031 isl_stream_error(s, tok, "expecting '-'");
1032 isl_stream_push_token(s, tok);
1033 return 0;
1034 case isl_yaml_sequence:
1035 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW)
1036 return isl_stream_eat_if_available(s, ',');
1037 tok = isl_stream_next_token(s);
1038 if (!tok)
1039 return 0;
1040 indent = tok->col - 1;
1041 if (indent < get_yaml_indent(s) || tok->type != '-') {
1042 isl_stream_push_token(s, tok);
1043 return 0;
1045 isl_token_free(tok);
1046 return 1;
1047 default:
1048 isl_die(s->ctx, isl_error_internal,
1049 "unexpected state", return 0);
1053 /* Start reading a YAML mapping.
1054 * Return 0 on success and -1 on error.
1056 * If the first token on the stream is a '{' then we remove this token
1057 * from the stream and keep track of the fact that the mapping
1058 * is given in flow format.
1059 * Otherwise, we assume the first token is the first key of the mapping and
1060 * keep track of its indentation, but keep the token on the stream.
1061 * In both cases, the next token we expect is the first key of the mapping.
1063 int isl_stream_yaml_read_start_mapping(__isl_keep isl_stream *s)
1065 struct isl_token *tok;
1066 int indent;
1068 if (push_state(s, isl_yaml_mapping_key_start) < 0)
1069 return -1;
1071 tok = isl_stream_next_token(s);
1072 if (!tok) {
1073 if (s->eof)
1074 isl_stream_error(s, NULL, "unexpected EOF");
1075 return -1;
1077 if (isl_token_get_type(tok) == '{') {
1078 isl_token_free(tok);
1079 return set_yaml_indent(s, ISL_YAML_INDENT_FLOW);
1081 indent = tok->col - 1;
1082 isl_stream_push_token(s, tok);
1084 return set_yaml_indent(s, indent);
1087 /* Finish reading a YAML mapping.
1088 * Return 0 on success and -1 on error.
1090 * If the mapping started with a '{', then we expect a '}' to close
1091 * the mapping.
1092 * Otherwise, we double-check that the next token (if any)
1093 * has a smaller indentation than that of the current mapping.
1095 int isl_stream_yaml_read_end_mapping(__isl_keep isl_stream *s)
1097 struct isl_token *tok;
1098 int indent;
1100 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) {
1101 if (isl_stream_eat(s, '}') < 0)
1102 return -1;
1103 return pop_state(s);
1106 tok = isl_stream_next_token(s);
1107 if (!tok)
1108 return pop_state(s);
1110 indent = tok->col - 1;
1111 isl_stream_push_token(s, tok);
1113 if (indent >= get_yaml_indent(s))
1114 isl_die(isl_stream_get_ctx(s), isl_error_invalid,
1115 "mapping not finished", return -1);
1117 return pop_state(s);
1120 /* Start reading a YAML sequence.
1121 * Return 0 on success and -1 on error.
1123 * If the first token on the stream is a '[' then we remove this token
1124 * from the stream and keep track of the fact that the sequence
1125 * is given in flow format.
1126 * Otherwise, we assume the first token is the dash that introduces
1127 * the first element of the sequence and keep track of its indentation,
1128 * but keep the token on the stream.
1129 * In both cases, the next token we expect is the first element
1130 * of the sequence.
1132 int isl_stream_yaml_read_start_sequence(__isl_keep isl_stream *s)
1134 struct isl_token *tok;
1135 int indent;
1137 if (push_state(s, isl_yaml_sequence_start) < 0)
1138 return -1;
1140 tok = isl_stream_next_token(s);
1141 if (!tok) {
1142 if (s->eof)
1143 isl_stream_error(s, NULL, "unexpected EOF");
1144 return -1;
1146 if (isl_token_get_type(tok) == '[') {
1147 isl_token_free(tok);
1148 return set_yaml_indent(s, ISL_YAML_INDENT_FLOW);
1150 indent = tok->col - 1;
1151 isl_stream_push_token(s, tok);
1153 return set_yaml_indent(s, indent);
1156 /* Finish reading a YAML sequence.
1157 * Return 0 on success and -1 on error.
1159 * If the sequence started with a '[', then we expect a ']' to close
1160 * the sequence.
1161 * Otherwise, we double-check that the next token (if any)
1162 * is not a dash or that it has a smaller indentation than
1163 * that of the current sequence.
1165 int isl_stream_yaml_read_end_sequence(__isl_keep isl_stream *s)
1167 struct isl_token *tok;
1168 int indent;
1169 int dash;
1171 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) {
1172 if (isl_stream_eat(s, ']') < 0)
1173 return -1;
1174 return pop_state(s);
1177 tok = isl_stream_next_token(s);
1178 if (!tok)
1179 return pop_state(s);
1181 indent = tok->col - 1;
1182 dash = tok->type == '-';
1183 isl_stream_push_token(s, tok);
1185 if (indent >= get_yaml_indent(s) && dash)
1186 isl_die(isl_stream_get_ctx(s), isl_error_invalid,
1187 "sequence not finished", return -1);
1189 return pop_state(s);