1/*
2 * Copyright 2008-2009 Katholieke Universiteit Leuven
3 *
4 * Use of this software is governed by the MIT license
5 *
6 * Written by Sven Verdoolaege, K.U.Leuven, Departement
7 * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium
8 */
9
10#include <ctype.h>
11#include <string.h>
12#include <isl_ctx_private.h>
13#include <isl_stream_private.h>
14#include <isl/map.h>
15#include <isl/aff.h>
16#include <isl_val_private.h>
17#include <isl_options_private.h>
18
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(b: !strcmp(s1: keyword->name, s2: 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(ctx: s->ctx, min_size: 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(), s: name);
46
47 entry = isl_hash_table_find(ctx: s->ctx, table: s->keywords, key_hash: name_hash,
48 eq: same_name, val: name, reserve: 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(s: name);
61 if (!keyword->name) {
62 free(ptr: 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, n: 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(b: 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(s: 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(map: tok->u.map);
135 else if (tok->type == ISL_TOKEN_AFF)
136 isl_pw_aff_free(pwaff: tok->u.pwaff);
137 else
138 free(ptr: tok->u.s);
139 free(ptr: 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(ctx: s->ctx, error: isl_error_invalid, msg: "syntax error",
149 __FILE__, __LINE__);
150
151 if (s->ctx->opt->on_error == ISL_ON_ERROR_CONTINUE)
152 return;
153 fprintf(stderr, format: "syntax error (%d, %d): %s\n", line, col, msg);
154 if (tok) {
155 if (tok->type < 256)
156 fprintf(stderr, format: "got '%c'\n", tok->type);
157 else if (tok->type == ISL_TOKEN_IDENT)
158 fprintf(stderr, format: "got ident '%s'\n", tok->u.s);
159 else if (tok->is_keyword)
160 fprintf(stderr, format: "got keyword '%s'\n", tok->u.s);
161 else if (tok->type == ISL_TOKEN_VALUE) {
162 fprintf(stderr, format: "got value '");
163 isl_int_print(stderr, tok->u.v, 0);
164 fprintf(stderr, format: "'\n");
165 } else if (tok->type == ISL_TOKEN_MAP) {
166 isl_printer *p;
167 fprintf(stderr, format: "got map '");
168 p = isl_printer_to_file(ctx: s->ctx, stderr);
169 p = isl_printer_print_map(printer: p, map: tok->u.map);
170 isl_printer_free(printer: p);
171 fprintf(stderr, format: "'\n");
172 } else if (tok->type == ISL_TOKEN_AFF) {
173 isl_printer *p;
174 fprintf(stderr, format: "got affine expression '");
175 p = isl_printer_to_file(ctx: s->ctx, stderr);
176 p = isl_printer_print_pw_aff(p, pwaff: tok->u.pwaff);
177 isl_printer_free(printer: p);
178 fprintf(stderr, format: "'\n");
179 } else if (tok->u.s)
180 fprintf(stderr, format: "got token '%s'\n", tok->u.s);
181 else
182 fprintf(stderr, format: "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(ctx: 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->col
241 * 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(stream: s->file);
252 else {
253 c = *s->str++;
254 if (c == '\0')
255 c = -1;
256 }
257 if (c == -1)
258 s->eof = 1;
259 else if (c == '\n') {
260 s->line++;
261 s->col = 1;
262 } else
263 s->col++;
264 s->c = c;
265 return c;
266}
267
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 column
277 * 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(s1: s->buffer, s2: "exists"))
324 return ISL_TOKEN_EXISTS;
325 if (!strcasecmp(s1: s->buffer, s2: "and"))
326 return ISL_TOKEN_AND;
327 if (!strcasecmp(s1: s->buffer, s2: "or"))
328 return ISL_TOKEN_OR;
329 if (!strcasecmp(s1: s->buffer, s2: "implies"))
330 return ISL_TOKEN_IMPLIES;
331 if (!strcasecmp(s1: s->buffer, s2: "not"))
332 return ISL_TOKEN_NOT;
333 if (!strcasecmp(s1: s->buffer, s2: "infty"))
334 return ISL_TOKEN_INFTY;
335 if (!strcasecmp(s1: s->buffer, s2: "infinity"))
336 return ISL_TOKEN_INFTY;
337 if (!strcasecmp(s1: s->buffer, s2: "NaN"))
338 return ISL_TOKEN_NAN;
339 if (!strcasecmp(s1: s->buffer, s2: "min"))
340 return ISL_TOKEN_MIN;
341 if (!strcasecmp(s1: s->buffer, s2: "max"))
342 return ISL_TOKEN_MAX;
343 if (!strcasecmp(s1: s->buffer, s2: "rat"))
344 return ISL_TOKEN_RAT;
345 if (!strcasecmp(s1: s->buffer, s2: "true"))
346 return ISL_TOKEN_TRUE;
347 if (!strcasecmp(s1: s->buffer, s2: "false"))
348 return ISL_TOKEN_FALSE;
349 if (!strcasecmp(s1: s->buffer, s2: "ceild"))
350 return ISL_TOKEN_CEILD;
351 if (!strcasecmp(s1: s->buffer, s2: "floord"))
352 return ISL_TOKEN_FLOORD;
353 if (!strcasecmp(s1: s->buffer, s2: "mod"))
354 return ISL_TOKEN_MOD;
355 if (!strcasecmp(s1: s->buffer, s2: "ceil"))
356 return ISL_TOKEN_CEIL;
357 if (!strcasecmp(s1: s->buffer, s2: "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: s->buffer);
364 entry = isl_hash_table_find(ctx: s->ctx, table: s->keywords, key_hash: name_hash, eq: same_name,
365 val: s->buffer, reserve: 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(ctx: s->ctx, line, col, on_new_line: 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(ctx: s->ctx, line, col, on_new_line: old_line != line);
450 if (!tok)
451 return NULL;
452 tok->u.s = strdup(s: "->");
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(ctx: s->ctx, line, col, on_new_line: 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(ctx: s->ctx, line, col, on_new_line: 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, c: '\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(ctx: s->ctx, line, col, on_new_line: 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(ctx: s->ctx, line, col, on_new_line: 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, c: '\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: s->buffer);
510 if (!tok->u.s)
511 goto error;
512 return tok;
513 }
514 if (c == '"') {
515 tok = isl_token_new(ctx: s->ctx, line, col, on_new_line: 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, msg: "unterminated string");
524 goto error;
525 }
526 isl_stream_push_char(s, c: '\0');
527 tok->u.s = strdup(s: s->buffer);
528 return tok;
529 }
530 if (c == '=') {
531 int c;
532 tok = isl_token_new(ctx: s->ctx, line, col, on_new_line: old_line != line);
533 if (!tok)
534 return NULL;
535 if ((c = isl_stream_getc(s)) == '=') {
536 tok->u.s = strdup(s: "==");
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(ctx: s->ctx, line, col, on_new_line: old_line != line);
548 if (!tok)
549 return NULL;
550 if ((c = isl_stream_getc(s)) == '=') {
551 tok->u.s = strdup(s: ":=");
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(ctx: s->ctx, line, col, on_new_line: old_line != line);
563 if (!tok)
564 return NULL;
565 if ((c = isl_stream_getc(s)) == '=') {
566 tok->u.s = strdup(s: ">=");
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(s: ">>=");
572 tok->type = ISL_TOKEN_LEX_GE;
573 return tok;
574 }
575 tok->u.s = strdup(s: ">>");
576 tok->type = ISL_TOKEN_LEX_GT;
577 } else {
578 tok->u.s = strdup(s: ">");
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(ctx: s->ctx, line, col, on_new_line: old_line != line);
588 if (!tok)
589 return NULL;
590 if ((c = isl_stream_getc(s)) == '=') {
591 tok->u.s = strdup(s: "<=");
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(s: "<<=");
597 tok->type = ISL_TOKEN_LEX_LE;
598 return tok;
599 }
600 tok->u.s = strdup(s: "<<");
601 tok->type = ISL_TOKEN_LEX_LT;
602 } else {
603 tok->u.s = strdup(s: "<");
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(ctx: s->ctx, line, col, on_new_line: 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(s: "&");
617 isl_stream_ungetc(s, c);
618 } else
619 tok->u.s = strdup(s: "&&");
620 return tok;
621 }
622 if (c == '|') {
623 tok = isl_token_new(ctx: s->ctx, line, col, on_new_line: 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(s: "|");
629 isl_stream_ungetc(s, c);
630 } else
631 tok->u.s = strdup(s: "||");
632 return tok;
633 }
634 if (c == '/') {
635 tok = isl_token_new(ctx: s->ctx, line, col, on_new_line: old_line != line);
636 if (!tok)
637 return NULL;
638 if ((c = isl_stream_getc(s)) == '\\') {
639 tok->u.s = strdup(s: "/\\");
640 tok->type = ISL_TOKEN_AND;
641 return tok;
642 } else if (c == '/') {
643 tok->u.s = strdup(s: "//");
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(ctx: s->ctx, line, col, on_new_line: 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(s: "\\/");
662 tok->type = ISL_TOKEN_OR;
663 }
664 return tok;
665 }
666 if (c == '!') {
667 tok = isl_token_new(ctx: s->ctx, line, col, on_new_line: old_line != line);
668 if (!tok)
669 return NULL;
670 if ((c = isl_stream_getc(s)) == '=') {
671 tok->u.s = strdup(s: "!=");
672 tok->type = ISL_TOKEN_NE;
673 return tok;
674 } else {
675 tok->type = ISL_TOKEN_NOT;
676 tok->u.s = strdup(s: "!");
677 }
678 if (c != -1)
679 isl_stream_ungetc(s, c);
680 return tok;
681 }
682
683 tok = isl_token_new(ctx: s->ctx, line, col, on_new_line: 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, same_line: 0);
696}
697
698struct isl_token *isl_stream_next_token_on_same_line(__isl_keep isl_stream *s)
699{
700 return next_token(s, same_line: 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(s: 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, msg: "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, msg: "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(ptr: keyword->name);
784 free(ptr: 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(tok: 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(ptr: s->buffer);
810 if (s->n_token != 0) {
811 struct isl_token *tok = isl_stream_next_token(s);
812 isl_stream_error(s, tok, msg: "unexpected token");
813 isl_token_free(tok);
814 }
815 if (s->keywords) {
816 isl_hash_table_foreach(ctx: s->ctx, table: s->keywords, fn: &free_keyword, NULL);
817 isl_hash_table_free(ctx: s->ctx, table: s->keywords);
818 }
819 free(ptr: s->yaml_state);
820 free(ptr: s->yaml_indent);
821 isl_ctx_deref(ctx: s->ctx);
822 free(ptr: 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 means
902 * 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 element
916 * 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 just
933 * started a mapping and we are expecting a key. If the mapping started
934 * with a '{', then we check if the next token is a '}'. If so,
935 * then the mapping is empty and there is no next state at this level.
936 * Otherwise, we assume that there is at least one key (the one from
937 * which we derived the indentation in isl_stream_yaml_read_start_mapping.
938 *
939 * If we are in state isl_yaml_mapping_key, then the we expect a colon
940 * followed by a value, so there is always a next state unless
941 * some error occurs.
942 *
943 * If we are in state isl_yaml_mapping_val, then there may or may
944 * not be a subsequent key in the same mapping.
945 * In flow format, the next key is preceded by a comma.
946 * In block format, the next key has the same indentation as the first key.
947 * If the first token has a smaller indentation, then we have reached
948 * the end of the current mapping.
949 *
950 * If we are in state isl_yaml_sequence_start, then we have just
951 * started a sequence. If the sequence started with a '[',
952 * then we check if the next token is a ']'. If so, then the sequence
953 * is empty and there is no next state at this level.
954 * Otherwise, we assume that there is at least one element in the sequence
955 * (the one from which we derived the indentation in
956 * isl_stream_yaml_read_start_sequence.
957 *
958 * If we are in state isl_yaml_sequence, then there may or may
959 * not be a subsequent element in the same sequence.
960 * In flow format, the next element is preceded by a comma.
961 * In block format, the next element is introduced by a dash with
962 * the same indentation as that of the first element.
963 * If the first token is not a dash or if it has a smaller indentation,
964 * then we have reached the end of the current sequence.
965 */
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, type: '}'))
980 return isl_bool_false;
981 if (update_state(s, state: 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, msg: "unexpected EOF");
989 return isl_bool_error;
990 }
991 if (tok->type == ':') {
992 isl_token_free(tok);
993 if (update_state(s, state: isl_yaml_mapping_val) < 0)
994 return isl_bool_error;
995 return isl_bool_true;
996 }
997 isl_stream_error(s, tok, msg: "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, type: ','))
1003 return isl_bool_false;
1004 if (update_state(s, state: 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, state: 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, type: ']'))
1021 return isl_bool_false;
1022 if (update_state(s, state: 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, msg: "unexpected EOF");
1030 return isl_bool_error;
1031 }
1032 if (tok->type == '-') {
1033 isl_token_free(tok);
1034 if (update_state(s, state: isl_yaml_sequence) < 0)
1035 return isl_bool_error;
1036 return isl_bool_true;
1037 }
1038 isl_stream_error(s, tok, msg: "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(b: isl_stream_eat_if_available(s, type: ','));
1044 tok = isl_stream_next_token(s);
1045 if (!tok)
1046 return isl_bool_false;
1047 indent = tok->col - 1;
1048 if (indent < get_yaml_indent(s) || tok->type != '-') {
1049 isl_stream_push_token(s, tok);
1050 return isl_bool_false;
1051 }
1052 isl_token_free(tok);
1053 return isl_bool_true;
1054 default:
1055 isl_die(s->ctx, isl_error_internal,
1056 "unexpected state", return isl_bool_error);
1057 }
1058}
1059
1060/* Start reading a YAML mapping.
1061 * Return isl_stat_ok on success and isl_stat_error on error.
1062 *
1063 * If the first token on the stream is a '{' then we remove this token
1064 * from the stream and keep track of the fact that the mapping
1065 * is given in flow format.
1066 * Otherwise, we assume the first token is the first key of the mapping and
1067 * keep track of its indentation, but keep the token on the stream.
1068 * In both cases, the next token we expect is the first key of the mapping.
1069 */
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, state: 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, msg: "unexpected EOF");
1082 return isl_stat_error;
1083 }
1084 if (isl_token_get_type(tok) == '{') {
1085 isl_token_free(tok);
1086 return set_yaml_indent(s, ISL_YAML_INDENT_FLOW);
1087 }
1088 indent = tok->col - 1;
1089 isl_stream_push_token(s, tok);
1090
1091 return set_yaml_indent(s, indent);
1092}
1093
1094/* Finish reading a YAML mapping.
1095 * Return isl_stat_ok on success and isl_stat_error on error.
1096 *
1097 * If the mapping started with a '{', then we expect a '}' to close
1098 * the mapping.
1099 * Otherwise, we double-check that the next token (if any)
1100 * has a smaller indentation than that of the current mapping.
1101 */
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, type: '}') < 0)
1109 return isl_stat_error;
1110 return pop_state(s);
1111 }
1112
1113 tok = isl_stream_next_token(s);
1114 if (!tok)
1115 return pop_state(s);
1116
1117 indent = tok->col - 1;
1118 isl_stream_push_token(s, tok);
1119
1120 if (indent >= get_yaml_indent(s))
1121 isl_die(isl_stream_get_ctx(s), isl_error_invalid,
1122 "mapping not finished", return isl_stat_error);
1123
1124 return pop_state(s);
1125}
1126
1127/* Start reading a YAML sequence.
1128 * Return isl_stat_ok on success and isl_stat_error on error.
1129 *
1130 * If the first token on the stream is a '[' then we remove this token
1131 * from the stream and keep track of the fact that the sequence
1132 * is given in flow format.
1133 * Otherwise, we assume the first token is the dash that introduces
1134 * the first element of the sequence and keep track of its indentation,
1135 * but keep the token on the stream.
1136 * In both cases, the next token we expect is the first element
1137 * of the sequence.
1138 */
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, state: 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, msg: "unexpected EOF");
1151 return isl_stat_error;
1152 }
1153 if (isl_token_get_type(tok) == '[') {
1154 isl_token_free(tok);
1155 return set_yaml_indent(s, ISL_YAML_INDENT_FLOW);
1156 }
1157 indent = tok->col - 1;
1158 isl_stream_push_token(s, tok);
1159
1160 return set_yaml_indent(s, indent);
1161}
1162
1163/* Finish reading a YAML sequence.
1164 * Return isl_stat_ok on success and isl_stat_error on error.
1165 *
1166 * If the sequence started with a '[', then we expect a ']' to close
1167 * the sequence.
1168 * Otherwise, we double-check that the next token (if any)
1169 * is not a dash or that it has a smaller indentation than
1170 * that of the current sequence.
1171 */
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, type: ']') < 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

source code of polly/lib/External/isl/isl_stream.c