isl 0.27
[isl.git] / isl_stream.c
blob9fffd45bf152118f026114c831a54f6fbeffa4af
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 /* Does the given token have a string representation?
106 isl_bool isl_token_has_str(struct isl_token *tok)
108 if (!tok)
109 return isl_bool_error;
110 return isl_bool_ok(tok->u.s != NULL);
113 /* Given a token with a string representation, return a copy of this string.
115 __isl_give char *isl_token_get_str(isl_ctx *ctx, struct isl_token *tok)
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);
124 return strdup(tok->u.s);
127 void isl_token_free(struct isl_token *tok)
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);
142 void isl_stream_error(__isl_keep isl_stream *s, struct isl_token *tok,
143 char *msg)
145 int line = tok ? tok->line : s->line;
146 int col = tok ? tok->col : s->col;
148 isl_ctx_set_full_error(s->ctx, isl_error_invalid, "syntax error",
149 __FILE__, __LINE__);
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);
184 if (s->ctx->opt->on_error == ISL_ON_ERROR_ABORT)
185 abort();
188 static __isl_give isl_stream* isl_stream_new(struct isl_ctx *ctx)
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;
219 __isl_give isl_stream* isl_stream_new_file(struct isl_ctx *ctx, FILE *file)
221 isl_stream *s = isl_stream_new(ctx);
222 if (!s)
223 return NULL;
224 s->file = file;
225 return s;
228 __isl_give isl_stream* isl_stream_new_str(struct isl_ctx *ctx, const char *str)
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;
240 /* Read a character from the stream and advance s->line and s->col
241 * to point to the next character.
243 static int stream_getc(__isl_keep isl_stream *s)
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;
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;
268 static void isl_stream_ungetc(__isl_keep isl_stream *s, int c)
270 isl_assert(s->ctx, s->n_un < 5, return);
271 s->un[s->n_un++] = c;
272 s->c = -1;
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.
279 static int isl_stream_getc(__isl_keep isl_stream *s)
281 int c;
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');
292 isl_stream_ungetc(s, c);
294 return '\\';
297 static int isl_stream_push_char(__isl_keep isl_stream *s, int c)
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;
307 s->buffer[s->len++] = c;
308 return 0;
311 void isl_stream_push_token(__isl_keep isl_stream *s, struct isl_token *tok)
313 isl_assert(s->ctx, s->n_token < 5, return);
314 s->tokens[s->n_token++] = tok;
317 static enum isl_token_type check_keywords(__isl_keep isl_stream *s)
319 struct isl_hash_table_entry *entry;
320 struct isl_keyword *keyword;
321 uint32_t name_hash;
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;
360 if (!s->keywords)
361 return ISL_TOKEN_IDENT;
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;
373 return ISL_TOKEN_IDENT;
376 int isl_stream_skip_line(__isl_keep isl_stream *s)
378 int c;
380 while ((c = isl_stream_getc(s)) != -1 && c != '\n')
381 /* nothing */
384 return c == -1 ? -1 : 0;
387 static struct isl_token *next_token(__isl_keep isl_stream *s, int same_line)
389 int c;
390 struct isl_token *tok = NULL;
391 int line, col;
392 int old_line = s->last_line;
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];
400 if (same_line && s->c == '\n')
401 return NULL;
403 s->len = 0;
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;
417 line = s->start_line;
418 col = s->start_col;
420 if (c == -1 || (same_line && c == '\n'))
421 return NULL;
422 s->last_line = line;
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;
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;
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;
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) '-';
489 return tok;
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;
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;
526 isl_stream_push_char(s, '\0');
527 tok->u.s = strdup(s->buffer);
528 return tok;
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;
540 if (c != -1)
541 isl_stream_ungetc(s, c);
542 tok->type = (enum isl_token_type) '=';
543 return tok;
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;
555 if (c != -1)
556 isl_stream_ungetc(s, c);
557 tok->type = (enum isl_token_type) ':';
558 return tok;
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;
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;
581 if (c != -1)
582 isl_stream_ungetc(s, c);
583 return tok;
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;
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;
606 if (c != -1)
607 isl_stream_ungetc(s, c);
608 return tok;
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;
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;
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) '/';
649 if (c != -1)
650 isl_stream_ungetc(s, c);
651 return tok;
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;
664 return tok;
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("!");
678 if (c != -1)
679 isl_stream_ungetc(s, c);
680 return tok;
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;
693 struct isl_token *isl_stream_next_token(__isl_keep isl_stream *s)
695 return next_token(s, 0);
698 struct isl_token *isl_stream_next_token_on_same_line(__isl_keep isl_stream *s)
700 return next_token(s, 1);
703 int isl_stream_eat_if_available(__isl_keep isl_stream *s, int type)
705 struct isl_token *tok;
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;
714 isl_stream_push_token(s, tok);
715 return 0;
718 int isl_stream_next_token_is(__isl_keep isl_stream *s, int type)
720 struct isl_token *tok;
721 int r;
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;
731 char *isl_stream_read_ident_if_available(__isl_keep isl_stream *s)
733 struct isl_token *tok;
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;
743 isl_stream_push_token(s, tok);
744 return NULL;
747 int isl_stream_eat(__isl_keep isl_stream *s, int type)
749 struct isl_token *tok;
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;
757 if (tok->type == type) {
758 isl_token_free(tok);
759 return 0;
761 isl_stream_error(s, tok, "expecting other token");
762 isl_token_free(tok);
763 return -1;
766 int isl_stream_is_empty(__isl_keep isl_stream *s)
768 struct isl_token *tok;
770 tok = isl_stream_next_token(s);
772 if (!tok)
773 return 1;
775 isl_stream_push_token(s, tok);
776 return 0;
779 static isl_stat free_keyword(void **p, void *user)
781 struct isl_keyword *keyword = *p;
783 free(keyword->name);
784 free(keyword);
786 return isl_stat_ok;
789 void isl_stream_flush_tokens(__isl_keep isl_stream *s)
791 int i;
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;
800 isl_ctx *isl_stream_get_ctx(__isl_keep isl_stream *s)
802 return s ? s->ctx : NULL;
805 void isl_stream_free(__isl_take isl_stream *s)
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);
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);
819 free(s->yaml_state);
820 free(s->yaml_indent);
821 isl_ctx_deref(s->ctx);
822 free(s);
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.
829 static int push_state(__isl_keep isl_stream *s, enum isl_yaml_state state)
831 if (s->yaml_size < s->yaml_depth + 1) {
832 int *indent;
833 enum isl_yaml_state *state;
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;
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;
847 s->yaml_size = s->yaml_depth + 1;
850 s->yaml_state[s->yaml_depth] = state;
851 s->yaml_depth++;
853 return 0;
856 /* Remove the innermost active YAML element from the stack.
857 * Return isl_stat_ok on success and isl_stat_error on failure.
859 static isl_stat pop_state(__isl_keep isl_stream *s)
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);
867 s->yaml_depth--;
869 return isl_stat_ok;
872 /* Set the state of the innermost active YAML element to "state".
873 * Return 0 on success and -1 on failure.
875 static int update_state(__isl_keep isl_stream *s, enum isl_yaml_state state)
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);
883 s->yaml_state[s->yaml_depth - 1] = state;
885 return 0;
888 /* Return the state of the innermost active YAML element.
889 * Return isl_yaml_none if we are not inside any YAML element.
891 static enum isl_yaml_state current_state(__isl_keep isl_stream *s)
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];
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.
904 static isl_stat set_yaml_indent(__isl_keep isl_stream *s, int indent)
906 if (s->yaml_depth < 1)
907 isl_die(s->ctx, isl_error_internal,
908 "not in YAML element", return isl_stat_error);
910 s->yaml_indent[s->yaml_depth - 1] = indent;
912 return isl_stat_ok;
915 /* Return the indentation of the innermost active YAML element
916 * of -1 on error.
918 static int get_yaml_indent(__isl_keep isl_stream *s)
920 if (s->yaml_depth < 1)
921 isl_die(s->ctx, isl_error_internal,
922 "not in YAML element", return -1);
924 return s->yaml_indent[s->yaml_depth - 1];
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.
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.
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.
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.
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.
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.
966 isl_bool isl_stream_yaml_next(__isl_keep isl_stream *s)
968 struct isl_token *tok;
969 enum isl_yaml_state state;
970 int indent;
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;
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;
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;
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;
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;
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;
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;
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);
1060 /* Start reading a YAML mapping.
1061 * Return isl_stat_ok on success and isl_stat_error on error.
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.
1070 isl_stat isl_stream_yaml_read_start_mapping(__isl_keep isl_stream *s)
1072 struct isl_token *tok;
1073 int indent;
1075 if (push_state(s, isl_yaml_mapping_key_start) < 0)
1076 return isl_stat_error;
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;
1084 if (isl_token_get_type(tok) == '{') {
1085 isl_token_free(tok);
1086 return set_yaml_indent(s, ISL_YAML_INDENT_FLOW);
1088 indent = tok->col - 1;
1089 isl_stream_push_token(s, tok);
1091 return set_yaml_indent(s, indent);
1094 /* Finish reading a YAML mapping.
1095 * Return isl_stat_ok on success and isl_stat_error on error.
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.
1102 isl_stat isl_stream_yaml_read_end_mapping(__isl_keep isl_stream *s)
1104 struct isl_token *tok;
1105 int indent;
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);
1113 tok = isl_stream_next_token(s);
1114 if (!tok)
1115 return pop_state(s);
1117 indent = tok->col - 1;
1118 isl_stream_push_token(s, tok);
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);
1124 return pop_state(s);
1127 /* Start reading a YAML sequence.
1128 * Return isl_stat_ok on success and isl_stat_error on error.
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.
1139 isl_stat isl_stream_yaml_read_start_sequence(__isl_keep isl_stream *s)
1141 struct isl_token *tok;
1142 int indent;
1144 if (push_state(s, isl_yaml_sequence_start) < 0)
1145 return isl_stat_error;
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;
1153 if (isl_token_get_type(tok) == '[') {
1154 isl_token_free(tok);
1155 return set_yaml_indent(s, ISL_YAML_INDENT_FLOW);
1157 indent = tok->col - 1;
1158 isl_stream_push_token(s, tok);
1160 return set_yaml_indent(s, indent);
1163 /* Finish reading a YAML sequence.
1164 * Return isl_stat_ok on success and isl_stat_error on error.
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.
1172 isl_stat isl_stream_yaml_read_end_sequence(__isl_keep isl_stream *s)
1174 struct isl_token *tok;
1175 int indent;
1176 int dash;
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);
1184 tok = isl_stream_next_token(s);
1185 if (!tok)
1186 return pop_state(s);
1188 indent = tok->col - 1;
1189 dash = tok->type == '-';
1190 isl_stream_push_token(s, tok);
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);
1196 return pop_state(s);