add tests for Python interface
[isl.git] / isl_stream.c
blobbeb06a64539d8eec4ad9eafde6b7511c9b6a0e1d
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.h>
13 #include <isl_stream_private.h>
14 #include <isl/map.h>
15 #include <isl/aff.h>
16 #include <isl_val_private.h>
18 struct isl_keyword {
19 char *name;
20 enum isl_token_type type;
23 static int same_name(const void *entry, const void *val)
25 const struct isl_keyword *keyword = (const struct isl_keyword *)entry;
27 return !strcmp(keyword->name, val);
30 enum isl_token_type isl_stream_register_keyword(__isl_keep isl_stream *s,
31 const char *name)
33 struct isl_hash_table_entry *entry;
34 struct isl_keyword *keyword;
35 uint32_t name_hash;
37 if (!s->keywords) {
38 s->keywords = isl_hash_table_alloc(s->ctx, 10);
39 if (!s->keywords)
40 return ISL_TOKEN_ERROR;
41 s->next_type = ISL_TOKEN_LAST;
44 name_hash = isl_hash_string(isl_hash_init(), name);
46 entry = isl_hash_table_find(s->ctx, s->keywords, name_hash,
47 same_name, name, 1);
48 if (!entry)
49 return ISL_TOKEN_ERROR;
50 if (entry->data) {
51 keyword = entry->data;
52 return keyword->type;
55 keyword = isl_calloc_type(s->ctx, struct isl_keyword);
56 if (!keyword)
57 return ISL_TOKEN_ERROR;
58 keyword->type = s->next_type++;
59 keyword->name = strdup(name);
60 if (!keyword->name) {
61 free(keyword);
62 return ISL_TOKEN_ERROR;
64 entry->data = keyword;
66 return keyword->type;
69 struct isl_token *isl_token_new(isl_ctx *ctx,
70 int line, int col, unsigned on_new_line)
72 struct isl_token *tok = isl_alloc_type(ctx, struct isl_token);
73 if (!tok)
74 return NULL;
75 tok->line = line;
76 tok->col = col;
77 tok->on_new_line = on_new_line;
78 tok->is_keyword = 0;
79 tok->u.s = NULL;
80 return tok;
83 /* Return the type of "tok".
85 int isl_token_get_type(struct isl_token *tok)
87 return tok ? tok->type : ISL_TOKEN_ERROR;
90 /* Given a token of type ISL_TOKEN_VALUE, return the value it represents.
92 __isl_give isl_val *isl_token_get_val(isl_ctx *ctx, struct isl_token *tok)
94 if (!tok)
95 return NULL;
96 if (tok->type != ISL_TOKEN_VALUE)
97 isl_die(ctx, isl_error_invalid, "not a value token",
98 return NULL);
100 return isl_val_int_from_isl_int(ctx, tok->u.v);
103 /* Given a token with a string representation, return a copy of this string.
105 __isl_give char *isl_token_get_str(isl_ctx *ctx, struct isl_token *tok)
107 if (!tok)
108 return NULL;
109 if (!tok->u.s)
110 isl_die(ctx, isl_error_invalid,
111 "token does not have a string representation",
112 return NULL);
114 return strdup(tok->u.s);
117 void isl_token_free(struct isl_token *tok)
119 if (!tok)
120 return;
121 if (tok->type == ISL_TOKEN_VALUE)
122 isl_int_clear(tok->u.v);
123 else if (tok->type == ISL_TOKEN_MAP)
124 isl_map_free(tok->u.map);
125 else if (tok->type == ISL_TOKEN_AFF)
126 isl_pw_aff_free(tok->u.pwaff);
127 else
128 free(tok->u.s);
129 free(tok);
132 void isl_stream_error(__isl_keep isl_stream *s, struct isl_token *tok,
133 char *msg)
135 int line = tok ? tok->line : s->line;
136 int col = tok ? tok->col : s->col;
137 fprintf(stderr, "syntax error (%d, %d): %s\n", line, col, msg);
138 if (tok) {
139 if (tok->type < 256)
140 fprintf(stderr, "got '%c'\n", tok->type);
141 else if (tok->type == ISL_TOKEN_IDENT)
142 fprintf(stderr, "got ident '%s'\n", tok->u.s);
143 else if (tok->is_keyword)
144 fprintf(stderr, "got keyword '%s'\n", tok->u.s);
145 else if (tok->type == ISL_TOKEN_VALUE) {
146 fprintf(stderr, "got value '");
147 isl_int_print(stderr, tok->u.v, 0);
148 fprintf(stderr, "'\n");
149 } else if (tok->type == ISL_TOKEN_MAP) {
150 isl_printer *p;
151 fprintf(stderr, "got map '");
152 p = isl_printer_to_file(s->ctx, stderr);
153 p = isl_printer_print_map(p, tok->u.map);
154 isl_printer_free(p);
155 fprintf(stderr, "'\n");
156 } else if (tok->type == ISL_TOKEN_AFF) {
157 isl_printer *p;
158 fprintf(stderr, "got affine expression '");
159 p = isl_printer_to_file(s->ctx, stderr);
160 p = isl_printer_print_pw_aff(p, tok->u.pwaff);
161 isl_printer_free(p);
162 fprintf(stderr, "'\n");
163 } else if (tok->u.s)
164 fprintf(stderr, "got token '%s'\n", tok->u.s);
165 else
166 fprintf(stderr, "got token type %d\n", tok->type);
170 static __isl_give isl_stream* isl_stream_new(struct isl_ctx *ctx)
172 int i;
173 isl_stream *s = isl_calloc_type(ctx, struct isl_stream);
174 if (!s)
175 return NULL;
176 s->ctx = ctx;
177 isl_ctx_ref(s->ctx);
178 s->file = NULL;
179 s->str = NULL;
180 s->len = 0;
181 s->line = 1;
182 s->col = 1;
183 s->eof = 0;
184 s->last_line = 0;
185 s->c = -1;
186 s->n_un = 0;
187 for (i = 0; i < 5; ++i)
188 s->tokens[i] = NULL;
189 s->n_token = 0;
190 s->keywords = NULL;
191 s->size = 256;
192 s->buffer = isl_alloc_array(ctx, char, s->size);
193 if (!s->buffer)
194 goto error;
195 return s;
196 error:
197 isl_stream_free(s);
198 return NULL;
201 __isl_give isl_stream* isl_stream_new_file(struct isl_ctx *ctx, FILE *file)
203 isl_stream *s = isl_stream_new(ctx);
204 if (!s)
205 return NULL;
206 s->file = file;
207 return s;
210 __isl_give isl_stream* isl_stream_new_str(struct isl_ctx *ctx, const char *str)
212 isl_stream *s;
213 if (!str)
214 return NULL;
215 s = isl_stream_new(ctx);
216 if (!s)
217 return NULL;
218 s->str = str;
219 return s;
222 /* Read a character from the stream and advance s->line and s->col
223 * to point to the next character.
225 static int stream_getc(__isl_keep isl_stream *s)
227 int c;
228 if (s->eof)
229 return -1;
230 if (s->n_un)
231 return s->c = s->un[--s->n_un];
232 if (s->file)
233 c = fgetc(s->file);
234 else {
235 c = *s->str++;
236 if (c == '\0')
237 c = -1;
239 if (c == -1)
240 s->eof = 1;
241 else if (c == '\n') {
242 s->line++;
243 s->col = 1;
244 } else
245 s->col++;
246 s->c = c;
247 return c;
250 static void isl_stream_ungetc(__isl_keep isl_stream *s, int c)
252 isl_assert(s->ctx, s->n_un < 5, return);
253 s->un[s->n_un++] = c;
254 s->c = -1;
257 /* Read a character from the stream, skipping pairs of '\\' and '\n'.
258 * Set s->start_line and s->start_col to the line and column
259 * of the returned character.
261 static int isl_stream_getc(__isl_keep isl_stream *s)
263 int c;
265 do {
266 s->start_line = s->line;
267 s->start_col = s->col;
268 c = stream_getc(s);
269 if (c != '\\')
270 return c;
271 c = stream_getc(s);
272 } while (c == '\n');
274 isl_stream_ungetc(s, c);
276 return '\\';
279 static int isl_stream_push_char(__isl_keep isl_stream *s, int c)
281 if (s->len >= s->size) {
282 char *buffer;
283 s->size = (3*s->size)/2;
284 buffer = isl_realloc_array(s->ctx, s->buffer, char, s->size);
285 if (!buffer)
286 return -1;
287 s->buffer = buffer;
289 s->buffer[s->len++] = c;
290 return 0;
293 void isl_stream_push_token(__isl_keep isl_stream *s, struct isl_token *tok)
295 isl_assert(s->ctx, s->n_token < 5, return);
296 s->tokens[s->n_token++] = tok;
299 static enum isl_token_type check_keywords(__isl_keep isl_stream *s)
301 struct isl_hash_table_entry *entry;
302 struct isl_keyword *keyword;
303 uint32_t name_hash;
305 if (!strcasecmp(s->buffer, "exists"))
306 return ISL_TOKEN_EXISTS;
307 if (!strcasecmp(s->buffer, "and"))
308 return ISL_TOKEN_AND;
309 if (!strcasecmp(s->buffer, "or"))
310 return ISL_TOKEN_OR;
311 if (!strcasecmp(s->buffer, "implies"))
312 return ISL_TOKEN_IMPLIES;
313 if (!strcasecmp(s->buffer, "not"))
314 return ISL_TOKEN_NOT;
315 if (!strcasecmp(s->buffer, "infty"))
316 return ISL_TOKEN_INFTY;
317 if (!strcasecmp(s->buffer, "infinity"))
318 return ISL_TOKEN_INFTY;
319 if (!strcasecmp(s->buffer, "NaN"))
320 return ISL_TOKEN_NAN;
321 if (!strcasecmp(s->buffer, "min"))
322 return ISL_TOKEN_MIN;
323 if (!strcasecmp(s->buffer, "max"))
324 return ISL_TOKEN_MAX;
325 if (!strcasecmp(s->buffer, "rat"))
326 return ISL_TOKEN_RAT;
327 if (!strcasecmp(s->buffer, "true"))
328 return ISL_TOKEN_TRUE;
329 if (!strcasecmp(s->buffer, "false"))
330 return ISL_TOKEN_FALSE;
331 if (!strcasecmp(s->buffer, "ceild"))
332 return ISL_TOKEN_CEILD;
333 if (!strcasecmp(s->buffer, "floord"))
334 return ISL_TOKEN_FLOORD;
335 if (!strcasecmp(s->buffer, "mod"))
336 return ISL_TOKEN_MOD;
337 if (!strcasecmp(s->buffer, "ceil"))
338 return ISL_TOKEN_CEIL;
339 if (!strcasecmp(s->buffer, "floor"))
340 return ISL_TOKEN_FLOOR;
342 if (!s->keywords)
343 return ISL_TOKEN_IDENT;
345 name_hash = isl_hash_string(isl_hash_init(), s->buffer);
346 entry = isl_hash_table_find(s->ctx, s->keywords, name_hash, same_name,
347 s->buffer, 0);
348 if (entry) {
349 keyword = entry->data;
350 return keyword->type;
353 return ISL_TOKEN_IDENT;
356 int isl_stream_skip_line(__isl_keep isl_stream *s)
358 int c;
360 while ((c = isl_stream_getc(s)) != -1 && c != '\n')
361 /* nothing */
364 return c == -1 ? -1 : 0;
367 static struct isl_token *next_token(__isl_keep isl_stream *s, int same_line)
369 int c;
370 struct isl_token *tok = NULL;
371 int line, col;
372 int old_line = s->last_line;
374 if (s->n_token) {
375 if (same_line && s->tokens[s->n_token - 1]->on_new_line)
376 return NULL;
377 return s->tokens[--s->n_token];
380 if (same_line && s->c == '\n')
381 return NULL;
383 s->len = 0;
385 /* skip spaces and comment lines */
386 while ((c = isl_stream_getc(s)) != -1) {
387 if (c == '#') {
388 if (isl_stream_skip_line(s) < 0)
389 break;
390 c = '\n';
391 if (same_line)
392 break;
393 } else if (!isspace(c) || (same_line && c == '\n'))
394 break;
397 line = s->start_line;
398 col = s->start_col;
400 if (c == -1 || (same_line && c == '\n'))
401 return NULL;
402 s->last_line = line;
404 if (c == '(' ||
405 c == ')' ||
406 c == '+' ||
407 c == '*' ||
408 c == '%' ||
409 c == '?' ||
410 c == '^' ||
411 c == '@' ||
412 c == '$' ||
413 c == ',' ||
414 c == '.' ||
415 c == ';' ||
416 c == '[' ||
417 c == ']' ||
418 c == '{' ||
419 c == '}') {
420 tok = isl_token_new(s->ctx, line, col, old_line != line);
421 if (!tok)
422 return NULL;
423 tok->type = (enum isl_token_type)c;
424 return tok;
426 if (c == '-') {
427 int c;
428 if ((c = isl_stream_getc(s)) == '>') {
429 tok = isl_token_new(s->ctx, line, col, old_line != line);
430 if (!tok)
431 return NULL;
432 tok->u.s = strdup("->");
433 tok->type = ISL_TOKEN_TO;
434 return tok;
436 if (c != -1)
437 isl_stream_ungetc(s, c);
438 if (!isdigit(c)) {
439 tok = isl_token_new(s->ctx, line, col, old_line != line);
440 if (!tok)
441 return NULL;
442 tok->type = (enum isl_token_type) '-';
443 return tok;
446 if (c == '-' || isdigit(c)) {
447 int minus = c == '-';
448 tok = isl_token_new(s->ctx, line, col, old_line != line);
449 if (!tok)
450 return NULL;
451 tok->type = ISL_TOKEN_VALUE;
452 isl_int_init(tok->u.v);
453 if (isl_stream_push_char(s, c))
454 goto error;
455 while ((c = isl_stream_getc(s)) != -1 && isdigit(c))
456 if (isl_stream_push_char(s, c))
457 goto error;
458 if (c != -1)
459 isl_stream_ungetc(s, c);
460 isl_stream_push_char(s, '\0');
461 isl_int_read(tok->u.v, s->buffer);
462 if (minus && isl_int_is_zero(tok->u.v)) {
463 tok->col++;
464 tok->on_new_line = 0;
465 isl_stream_push_token(s, tok);
466 tok = isl_token_new(s->ctx, line, col, old_line != line);
467 if (!tok)
468 return NULL;
469 tok->type = (enum isl_token_type) '-';
471 return tok;
473 if (isalpha(c) || c == '_') {
474 tok = isl_token_new(s->ctx, line, col, old_line != line);
475 if (!tok)
476 return NULL;
477 isl_stream_push_char(s, c);
478 while ((c = isl_stream_getc(s)) != -1 &&
479 (isalnum(c) || c == '_'))
480 isl_stream_push_char(s, c);
481 if (c != -1)
482 isl_stream_ungetc(s, c);
483 while ((c = isl_stream_getc(s)) != -1 && c == '\'')
484 isl_stream_push_char(s, c);
485 if (c != -1)
486 isl_stream_ungetc(s, c);
487 isl_stream_push_char(s, '\0');
488 tok->type = check_keywords(s);
489 if (tok->type != ISL_TOKEN_IDENT)
490 tok->is_keyword = 1;
491 tok->u.s = strdup(s->buffer);
492 if (!tok->u.s)
493 goto error;
494 return tok;
496 if (c == '"') {
497 tok = isl_token_new(s->ctx, line, col, old_line != line);
498 if (!tok)
499 return NULL;
500 tok->type = ISL_TOKEN_STRING;
501 tok->u.s = NULL;
502 while ((c = isl_stream_getc(s)) != -1 && c != '"' && c != '\n')
503 isl_stream_push_char(s, c);
504 if (c != '"') {
505 isl_stream_error(s, NULL, "unterminated string");
506 goto error;
508 isl_stream_push_char(s, '\0');
509 tok->u.s = strdup(s->buffer);
510 return tok;
512 if (c == '=') {
513 int c;
514 tok = isl_token_new(s->ctx, line, col, old_line != line);
515 if (!tok)
516 return NULL;
517 if ((c = isl_stream_getc(s)) == '=') {
518 tok->u.s = strdup("==");
519 tok->type = ISL_TOKEN_EQ_EQ;
520 return tok;
522 if (c != -1)
523 isl_stream_ungetc(s, c);
524 tok->type = (enum isl_token_type) '=';
525 return tok;
527 if (c == ':') {
528 int c;
529 tok = isl_token_new(s->ctx, line, col, old_line != line);
530 if (!tok)
531 return NULL;
532 if ((c = isl_stream_getc(s)) == '=') {
533 tok->u.s = strdup(":=");
534 tok->type = ISL_TOKEN_DEF;
535 return tok;
537 if (c != -1)
538 isl_stream_ungetc(s, c);
539 tok->type = (enum isl_token_type) ':';
540 return tok;
542 if (c == '>') {
543 int c;
544 tok = isl_token_new(s->ctx, line, col, old_line != line);
545 if (!tok)
546 return NULL;
547 if ((c = isl_stream_getc(s)) == '=') {
548 tok->u.s = strdup(">=");
549 tok->type = ISL_TOKEN_GE;
550 return tok;
551 } else if (c == '>') {
552 if ((c = isl_stream_getc(s)) == '=') {
553 tok->u.s = strdup(">>=");
554 tok->type = ISL_TOKEN_LEX_GE;
555 return tok;
557 tok->u.s = strdup(">>");
558 tok->type = ISL_TOKEN_LEX_GT;
559 } else {
560 tok->u.s = strdup(">");
561 tok->type = ISL_TOKEN_GT;
563 if (c != -1)
564 isl_stream_ungetc(s, c);
565 return tok;
567 if (c == '<') {
568 int c;
569 tok = isl_token_new(s->ctx, line, col, old_line != line);
570 if (!tok)
571 return NULL;
572 if ((c = isl_stream_getc(s)) == '=') {
573 tok->u.s = strdup("<=");
574 tok->type = ISL_TOKEN_LE;
575 return tok;
576 } else if (c == '<') {
577 if ((c = isl_stream_getc(s)) == '=') {
578 tok->u.s = strdup("<<=");
579 tok->type = ISL_TOKEN_LEX_LE;
580 return tok;
582 tok->u.s = strdup("<<");
583 tok->type = ISL_TOKEN_LEX_LT;
584 } else {
585 tok->u.s = strdup("<");
586 tok->type = ISL_TOKEN_LT;
588 if (c != -1)
589 isl_stream_ungetc(s, c);
590 return tok;
592 if (c == '&') {
593 tok = isl_token_new(s->ctx, line, col, old_line != line);
594 if (!tok)
595 return NULL;
596 tok->type = ISL_TOKEN_AND;
597 if ((c = isl_stream_getc(s)) != '&' && c != -1) {
598 tok->u.s = strdup("&");
599 isl_stream_ungetc(s, c);
600 } else
601 tok->u.s = strdup("&&");
602 return tok;
604 if (c == '|') {
605 tok = isl_token_new(s->ctx, line, col, old_line != line);
606 if (!tok)
607 return NULL;
608 tok->type = ISL_TOKEN_OR;
609 if ((c = isl_stream_getc(s)) != '|' && c != -1) {
610 tok->u.s = strdup("|");
611 isl_stream_ungetc(s, c);
612 } else
613 tok->u.s = strdup("||");
614 return tok;
616 if (c == '/') {
617 tok = isl_token_new(s->ctx, line, col, old_line != line);
618 if (!tok)
619 return NULL;
620 if ((c = isl_stream_getc(s)) != '\\' && c != -1) {
621 tok->type = (enum isl_token_type) '/';
622 isl_stream_ungetc(s, c);
623 } else {
624 tok->u.s = strdup("/\\");
625 tok->type = ISL_TOKEN_AND;
627 return tok;
629 if (c == '\\') {
630 tok = isl_token_new(s->ctx, line, col, old_line != line);
631 if (!tok)
632 return NULL;
633 if ((c = isl_stream_getc(s)) != '/' && c != -1) {
634 tok->type = (enum isl_token_type) '\\';
635 isl_stream_ungetc(s, c);
636 } else {
637 tok->u.s = strdup("\\/");
638 tok->type = ISL_TOKEN_OR;
640 return tok;
642 if (c == '!') {
643 tok = isl_token_new(s->ctx, line, col, old_line != line);
644 if (!tok)
645 return NULL;
646 if ((c = isl_stream_getc(s)) == '=') {
647 tok->u.s = strdup("!=");
648 tok->type = ISL_TOKEN_NE;
649 return tok;
650 } else {
651 tok->type = ISL_TOKEN_NOT;
652 tok->u.s = strdup("!");
654 if (c != -1)
655 isl_stream_ungetc(s, c);
656 return tok;
659 tok = isl_token_new(s->ctx, line, col, old_line != line);
660 if (!tok)
661 return NULL;
662 tok->type = ISL_TOKEN_UNKNOWN;
663 return tok;
664 error:
665 isl_token_free(tok);
666 return NULL;
669 struct isl_token *isl_stream_next_token(__isl_keep isl_stream *s)
671 return next_token(s, 0);
674 struct isl_token *isl_stream_next_token_on_same_line(__isl_keep isl_stream *s)
676 return next_token(s, 1);
679 int isl_stream_eat_if_available(__isl_keep isl_stream *s, int type)
681 struct isl_token *tok;
683 tok = isl_stream_next_token(s);
684 if (!tok)
685 return 0;
686 if (tok->type == type) {
687 isl_token_free(tok);
688 return 1;
690 isl_stream_push_token(s, tok);
691 return 0;
694 int isl_stream_next_token_is(__isl_keep isl_stream *s, int type)
696 struct isl_token *tok;
697 int r;
699 tok = isl_stream_next_token(s);
700 if (!tok)
701 return 0;
702 r = tok->type == type;
703 isl_stream_push_token(s, tok);
704 return r;
707 char *isl_stream_read_ident_if_available(__isl_keep isl_stream *s)
709 struct isl_token *tok;
711 tok = isl_stream_next_token(s);
712 if (!tok)
713 return NULL;
714 if (tok->type == ISL_TOKEN_IDENT) {
715 char *ident = strdup(tok->u.s);
716 isl_token_free(tok);
717 return ident;
719 isl_stream_push_token(s, tok);
720 return NULL;
723 int isl_stream_eat(__isl_keep isl_stream *s, int type)
725 struct isl_token *tok;
727 tok = isl_stream_next_token(s);
728 if (!tok) {
729 if (s->eof)
730 isl_stream_error(s, NULL, "unexpected EOF");
731 return -1;
733 if (tok->type == type) {
734 isl_token_free(tok);
735 return 0;
737 isl_stream_error(s, tok, "expecting other token");
738 isl_stream_push_token(s, tok);
739 return -1;
742 int isl_stream_is_empty(__isl_keep isl_stream *s)
744 struct isl_token *tok;
746 tok = isl_stream_next_token(s);
748 if (!tok)
749 return 1;
751 isl_stream_push_token(s, tok);
752 return 0;
755 static isl_stat free_keyword(void **p, void *user)
757 struct isl_keyword *keyword = *p;
759 free(keyword->name);
760 free(keyword);
762 return isl_stat_ok;
765 void isl_stream_flush_tokens(__isl_keep isl_stream *s)
767 int i;
769 if (!s)
770 return;
771 for (i = 0; i < s->n_token; ++i)
772 isl_token_free(s->tokens[i]);
773 s->n_token = 0;
776 isl_ctx *isl_stream_get_ctx(__isl_keep isl_stream *s)
778 return s ? s->ctx : NULL;
781 void isl_stream_free(__isl_take isl_stream *s)
783 if (!s)
784 return;
785 free(s->buffer);
786 if (s->n_token != 0) {
787 struct isl_token *tok = isl_stream_next_token(s);
788 isl_stream_error(s, tok, "unexpected token");
789 isl_token_free(tok);
791 if (s->keywords) {
792 isl_hash_table_foreach(s->ctx, s->keywords, &free_keyword, NULL);
793 isl_hash_table_free(s->ctx, s->keywords);
795 free(s->yaml_state);
796 free(s->yaml_indent);
797 isl_ctx_deref(s->ctx);
798 free(s);
801 /* Push "state" onto the stack of currently active YAML elements.
802 * The caller is responsible for setting the corresponding indentation.
803 * Return 0 on success and -1 on failure.
805 static int push_state(__isl_keep isl_stream *s, enum isl_yaml_state state)
807 if (s->yaml_size < s->yaml_depth + 1) {
808 int *indent;
809 enum isl_yaml_state *state;
811 state = isl_realloc_array(s->ctx, s->yaml_state,
812 enum isl_yaml_state, s->yaml_depth + 1);
813 if (!state)
814 return -1;
815 s->yaml_state = state;
817 indent = isl_realloc_array(s->ctx, s->yaml_indent,
818 int, s->yaml_depth + 1);
819 if (!indent)
820 return -1;
821 s->yaml_indent = indent;
823 s->yaml_size = s->yaml_depth + 1;
826 s->yaml_state[s->yaml_depth] = state;
827 s->yaml_depth++;
829 return 0;
832 /* Remove the innermost active YAML element from the stack.
833 * Return 0 on success and -1 on failure.
835 static int pop_state(__isl_keep isl_stream *s)
837 if (!s)
838 return -1;
839 if (s->yaml_depth < 1)
840 isl_die(isl_stream_get_ctx(s), isl_error_invalid,
841 "not in YAML construct", return -1);
843 s->yaml_depth--;
845 return 0;
848 /* Set the state of the innermost active YAML element to "state".
849 * Return 0 on success and -1 on failure.
851 static int update_state(__isl_keep isl_stream *s, enum isl_yaml_state state)
853 if (!s)
854 return -1;
855 if (s->yaml_depth < 1)
856 isl_die(isl_stream_get_ctx(s), isl_error_invalid,
857 "not in YAML construct", return -1);
859 s->yaml_state[s->yaml_depth - 1] = state;
861 return 0;
864 /* Return the state of the innermost active YAML element.
865 * Return isl_yaml_none if we are not inside any YAML element.
867 static enum isl_yaml_state current_state(__isl_keep isl_stream *s)
869 if (!s)
870 return isl_yaml_none;
871 if (s->yaml_depth < 1)
872 return isl_yaml_none;
873 return s->yaml_state[s->yaml_depth - 1];
876 /* Set the indentation of the innermost active YAML element to "indent".
877 * If "indent" is equal to ISL_YAML_INDENT_FLOW, then this means
878 * that the current elemient is in flow format.
880 static int set_yaml_indent(__isl_keep isl_stream *s, int indent)
882 if (s->yaml_depth < 1)
883 isl_die(s->ctx, isl_error_internal,
884 "not in YAML element", return -1);
886 s->yaml_indent[s->yaml_depth - 1] = indent;
888 return 0;
891 /* Return the indentation of the innermost active YAML element
892 * of -1 on error.
894 static int get_yaml_indent(__isl_keep isl_stream *s)
896 if (s->yaml_depth < 1)
897 isl_die(s->ctx, isl_error_internal,
898 "not in YAML element", return -1);
900 return s->yaml_indent[s->yaml_depth - 1];
903 /* Move to the next state at the innermost level.
904 * Return 1 if successful.
905 * Return 0 if we are at the end of the innermost level.
906 * Return -1 on error.
908 * If we are in state isl_yaml_mapping_key_start, then we have just
909 * started a mapping and we are expecting a key. If the mapping started
910 * with a '{', then we check if the next token is a '}'. If so,
911 * then the mapping is empty and there is no next state at this level.
912 * Otherwise, we assume that there is at least one key (the one from
913 * which we derived the indentation in isl_stream_yaml_read_start_mapping.
915 * If we are in state isl_yaml_mapping_key, then the we expect a colon
916 * followed by a value, so there is always a next state unless
917 * some error occurs.
919 * If we are in state isl_yaml_mapping_val, then there may or may
920 * not be a subsequent key in the same mapping.
921 * In flow format, the next key is preceded by a comma.
922 * In block format, the next key has the same indentation as the first key.
923 * If the first token has a smaller indentation, then we have reached
924 * the end of the current mapping.
926 * If we are in state isl_yaml_sequence_start, then we have just
927 * started a sequence. If the sequence started with a '[',
928 * then we check if the next token is a ']'. If so, then the sequence
929 * is empty and there is no next state at this level.
930 * Otherwise, we assume that there is at least one element in the sequence
931 * (the one from which we derived the indentation in
932 * isl_stream_yaml_read_start_sequence.
934 * If we are in state isl_yaml_sequence, then there may or may
935 * not be a subsequent element in the same sequence.
936 * In flow format, the next element is preceded by a comma.
937 * In block format, the next element is introduced by a dash with
938 * the same indentation as that of the first element.
939 * If the first token is not a dash or if it has a smaller indentation,
940 * then we have reached the end of the current sequence.
942 int isl_stream_yaml_next(__isl_keep isl_stream *s)
944 struct isl_token *tok;
945 enum isl_yaml_state state;
946 int indent;
948 state = current_state(s);
949 if (state == isl_yaml_none)
950 isl_die(s->ctx, isl_error_invalid,
951 "not in YAML element", return -1);
952 switch (state) {
953 case isl_yaml_mapping_key_start:
954 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW &&
955 isl_stream_next_token_is(s, '}'))
956 return 0;
957 if (update_state(s, isl_yaml_mapping_key) < 0)
958 return -1;
959 return 1;
960 case isl_yaml_mapping_key:
961 tok = isl_stream_next_token(s);
962 if (!tok) {
963 if (s->eof)
964 isl_stream_error(s, NULL, "unexpected EOF");
965 return -1;
967 if (tok->type == ':') {
968 isl_token_free(tok);
969 if (update_state(s, isl_yaml_mapping_val) < 0)
970 return -1;
971 return 1;
973 isl_stream_error(s, tok, "expecting ':'");
974 isl_stream_push_token(s, tok);
975 return -1;
976 case isl_yaml_mapping_val:
977 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) {
978 if (!isl_stream_eat_if_available(s, ','))
979 return 0;
980 if (update_state(s, isl_yaml_mapping_key) < 0)
981 return -1;
982 return 1;
984 tok = isl_stream_next_token(s);
985 if (!tok)
986 return 0;
987 indent = tok->col - 1;
988 isl_stream_push_token(s, tok);
989 if (indent < get_yaml_indent(s))
990 return 0;
991 if (update_state(s, isl_yaml_mapping_key) < 0)
992 return -1;
993 return 1;
994 case isl_yaml_sequence_start:
995 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) {
996 if (isl_stream_next_token_is(s, ']'))
997 return 0;
998 if (update_state(s, isl_yaml_sequence) < 0)
999 return -1;
1000 return 1;
1002 tok = isl_stream_next_token(s);
1003 if (!tok) {
1004 if (s->eof)
1005 isl_stream_error(s, NULL, "unexpected EOF");
1006 return -1;
1008 if (tok->type == '-') {
1009 isl_token_free(tok);
1010 if (update_state(s, isl_yaml_sequence) < 0)
1011 return -1;
1012 return 1;
1014 isl_stream_error(s, tok, "expecting '-'");
1015 isl_stream_push_token(s, tok);
1016 return 0;
1017 case isl_yaml_sequence:
1018 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW)
1019 return isl_stream_eat_if_available(s, ',');
1020 tok = isl_stream_next_token(s);
1021 if (!tok)
1022 return 0;
1023 indent = tok->col - 1;
1024 if (indent < get_yaml_indent(s) || tok->type != '-') {
1025 isl_stream_push_token(s, tok);
1026 return 0;
1028 isl_token_free(tok);
1029 return 1;
1030 default:
1031 isl_die(s->ctx, isl_error_internal,
1032 "unexpected state", return 0);
1036 /* Start reading a YAML mapping.
1037 * Return 0 on success and -1 on error.
1039 * If the first token on the stream is a '{' then we remove this token
1040 * from the stream and keep track of the fact that the mapping
1041 * is given in flow format.
1042 * Otherwise, we assume the first token is the first key of the mapping and
1043 * keep track of its indentation, but keep the token on the stream.
1044 * In both cases, the next token we expect is the first key of the mapping.
1046 int isl_stream_yaml_read_start_mapping(__isl_keep isl_stream *s)
1048 struct isl_token *tok;
1049 int indent;
1051 if (push_state(s, isl_yaml_mapping_key_start) < 0)
1052 return -1;
1054 tok = isl_stream_next_token(s);
1055 if (!tok) {
1056 if (s->eof)
1057 isl_stream_error(s, NULL, "unexpected EOF");
1058 return -1;
1060 if (isl_token_get_type(tok) == '{') {
1061 isl_token_free(tok);
1062 return set_yaml_indent(s, ISL_YAML_INDENT_FLOW);
1064 indent = tok->col - 1;
1065 isl_stream_push_token(s, tok);
1067 return set_yaml_indent(s, indent);
1070 /* Finish reading a YAML mapping.
1071 * Return 0 on success and -1 on error.
1073 * If the mapping started with a '{', then we expect a '}' to close
1074 * the mapping.
1075 * Otherwise, we double-check that the next token (if any)
1076 * has a smaller indentation than that of the current mapping.
1078 int isl_stream_yaml_read_end_mapping(__isl_keep isl_stream *s)
1080 struct isl_token *tok;
1081 int indent;
1083 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) {
1084 if (isl_stream_eat(s, '}') < 0)
1085 return -1;
1086 return pop_state(s);
1089 tok = isl_stream_next_token(s);
1090 if (!tok)
1091 return pop_state(s);
1093 indent = tok->col - 1;
1094 isl_stream_push_token(s, tok);
1096 if (indent >= get_yaml_indent(s))
1097 isl_die(isl_stream_get_ctx(s), isl_error_invalid,
1098 "mapping not finished", return -1);
1100 return pop_state(s);
1103 /* Start reading a YAML sequence.
1104 * Return 0 on success and -1 on error.
1106 * If the first token on the stream is a '[' then we remove this token
1107 * from the stream and keep track of the fact that the sequence
1108 * is given in flow format.
1109 * Otherwise, we assume the first token is the dash that introduces
1110 * the first element of the sequence and keep track of its indentation,
1111 * but keep the token on the stream.
1112 * In both cases, the next token we expect is the first element
1113 * of the sequence.
1115 int isl_stream_yaml_read_start_sequence(__isl_keep isl_stream *s)
1117 struct isl_token *tok;
1118 int indent;
1120 if (push_state(s, isl_yaml_sequence_start) < 0)
1121 return -1;
1123 tok = isl_stream_next_token(s);
1124 if (!tok) {
1125 if (s->eof)
1126 isl_stream_error(s, NULL, "unexpected EOF");
1127 return -1;
1129 if (isl_token_get_type(tok) == '[') {
1130 isl_token_free(tok);
1131 return set_yaml_indent(s, ISL_YAML_INDENT_FLOW);
1133 indent = tok->col - 1;
1134 isl_stream_push_token(s, tok);
1136 return set_yaml_indent(s, indent);
1139 /* Finish reading a YAML sequence.
1140 * Return 0 on success and -1 on error.
1142 * If the sequence started with a '[', then we expect a ']' to close
1143 * the sequence.
1144 * Otherwise, we double-check that the next token (if any)
1145 * is not a dash or that it has a smaller indentation than
1146 * that of the current sequence.
1148 int isl_stream_yaml_read_end_sequence(__isl_keep isl_stream *s)
1150 struct isl_token *tok;
1151 int indent;
1152 int dash;
1154 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) {
1155 if (isl_stream_eat(s, ']') < 0)
1156 return -1;
1157 return pop_state(s);
1160 tok = isl_stream_next_token(s);
1161 if (!tok)
1162 return pop_state(s);
1164 indent = tok->col - 1;
1165 dash = tok->type == '-';
1166 isl_stream_push_token(s, tok);
1168 if (indent >= get_yaml_indent(s) && dash)
1169 isl_die(isl_stream_get_ctx(s), isl_error_invalid,
1170 "sequence not finished", return -1);
1172 return pop_state(s);