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.h>
13 #include <isl_stream_private.h>
14 #include <isl/map.h>
15 #include <isl/aff.h>
16 #include <isl_val_private.h>
17
18 struct isl_keyword {
19 char *name;
20 enum isl_token_type type;
21 };
22
same_name(const void * entry,const void * val)23 static isl_bool same_name(const void *entry, const void *val)
24 {
25 const struct isl_keyword *keyword = (const struct isl_keyword *)entry;
26
27 return isl_bool_ok(!strcmp(keyword->name, val));
28 }
29
isl_stream_register_keyword(__isl_keep isl_stream * s,const char * name)30 enum isl_token_type isl_stream_register_keyword(__isl_keep isl_stream *s,
31 const char *name)
32 {
33 struct isl_hash_table_entry *entry;
34 struct isl_keyword *keyword;
35 uint32_t name_hash;
36
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;
42 }
43
44 name_hash = isl_hash_string(isl_hash_init(), name);
45
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;
53 }
54
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;
63 }
64 entry->data = keyword;
65
66 return keyword->type;
67 }
68
isl_token_new(isl_ctx * ctx,int line,int col,unsigned on_new_line)69 struct isl_token *isl_token_new(isl_ctx *ctx,
70 int line, int col, unsigned on_new_line)
71 {
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;
81 }
82
83 /* Return the type of "tok".
84 */
isl_token_get_type(struct isl_token * tok)85 int isl_token_get_type(struct isl_token *tok)
86 {
87 return tok ? tok->type : ISL_TOKEN_ERROR;
88 }
89
90 /* Given a token of type ISL_TOKEN_VALUE, return the value it represents.
91 */
isl_token_get_val(isl_ctx * ctx,struct isl_token * tok)92 __isl_give isl_val *isl_token_get_val(isl_ctx *ctx, struct isl_token *tok)
93 {
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);
99
100 return isl_val_int_from_isl_int(ctx, tok->u.v);
101 }
102
103 /* Given a token with a string representation, return a copy of this string.
104 */
isl_token_get_str(isl_ctx * ctx,struct isl_token * tok)105 __isl_give char *isl_token_get_str(isl_ctx *ctx, struct isl_token *tok)
106 {
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);
113
114 return strdup(tok->u.s);
115 }
116
isl_token_free(struct isl_token * tok)117 void isl_token_free(struct isl_token *tok)
118 {
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);
130 }
131
isl_stream_error(__isl_keep isl_stream * s,struct isl_token * tok,char * msg)132 void isl_stream_error(__isl_keep isl_stream *s, struct isl_token *tok,
133 char *msg)
134 {
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);
167 }
168 }
169
isl_stream_new(struct isl_ctx * ctx)170 static __isl_give isl_stream* isl_stream_new(struct isl_ctx *ctx)
171 {
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;
199 }
200
isl_stream_new_file(struct isl_ctx * ctx,FILE * file)201 __isl_give isl_stream* isl_stream_new_file(struct isl_ctx *ctx, FILE *file)
202 {
203 isl_stream *s = isl_stream_new(ctx);
204 if (!s)
205 return NULL;
206 s->file = file;
207 return s;
208 }
209
isl_stream_new_str(struct isl_ctx * ctx,const char * str)210 __isl_give isl_stream* isl_stream_new_str(struct isl_ctx *ctx, const char *str)
211 {
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;
220 }
221
222 /* Read a character from the stream and advance s->line and s->col
223 * to point to the next character.
224 */
stream_getc(__isl_keep isl_stream * s)225 static int stream_getc(__isl_keep isl_stream *s)
226 {
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;
238 }
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;
248 }
249
isl_stream_ungetc(__isl_keep isl_stream * s,int c)250 static void isl_stream_ungetc(__isl_keep isl_stream *s, int c)
251 {
252 isl_assert(s->ctx, s->n_un < 5, return);
253 s->un[s->n_un++] = c;
254 s->c = -1;
255 }
256
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.
260 */
isl_stream_getc(__isl_keep isl_stream * s)261 static int isl_stream_getc(__isl_keep isl_stream *s)
262 {
263 int c;
264
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');
273
274 isl_stream_ungetc(s, c);
275
276 return '\\';
277 }
278
isl_stream_push_char(__isl_keep isl_stream * s,int c)279 static int isl_stream_push_char(__isl_keep isl_stream *s, int c)
280 {
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;
288 }
289 s->buffer[s->len++] = c;
290 return 0;
291 }
292
isl_stream_push_token(__isl_keep isl_stream * s,struct isl_token * tok)293 void isl_stream_push_token(__isl_keep isl_stream *s, struct isl_token *tok)
294 {
295 isl_assert(s->ctx, s->n_token < 5, return);
296 s->tokens[s->n_token++] = tok;
297 }
298
check_keywords(__isl_keep isl_stream * s)299 static enum isl_token_type check_keywords(__isl_keep isl_stream *s)
300 {
301 struct isl_hash_table_entry *entry;
302 struct isl_keyword *keyword;
303 uint32_t name_hash;
304
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;
341
342 if (!s->keywords)
343 return ISL_TOKEN_IDENT;
344
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 return ISL_TOKEN_ERROR;
350 if (entry != isl_hash_table_entry_none) {
351 keyword = entry->data;
352 return keyword->type;
353 }
354
355 return ISL_TOKEN_IDENT;
356 }
357
isl_stream_skip_line(__isl_keep isl_stream * s)358 int isl_stream_skip_line(__isl_keep isl_stream *s)
359 {
360 int c;
361
362 while ((c = isl_stream_getc(s)) != -1 && c != '\n')
363 /* nothing */
364 ;
365
366 return c == -1 ? -1 : 0;
367 }
368
next_token(__isl_keep isl_stream * s,int same_line)369 static struct isl_token *next_token(__isl_keep isl_stream *s, int same_line)
370 {
371 int c;
372 struct isl_token *tok = NULL;
373 int line, col;
374 int old_line = s->last_line;
375
376 if (s->n_token) {
377 if (same_line && s->tokens[s->n_token - 1]->on_new_line)
378 return NULL;
379 return s->tokens[--s->n_token];
380 }
381
382 if (same_line && s->c == '\n')
383 return NULL;
384
385 s->len = 0;
386
387 /* skip spaces and comment lines */
388 while ((c = isl_stream_getc(s)) != -1) {
389 if (c == '#') {
390 if (isl_stream_skip_line(s) < 0)
391 break;
392 c = '\n';
393 if (same_line)
394 break;
395 } else if (!isspace(c) || (same_line && c == '\n'))
396 break;
397 }
398
399 line = s->start_line;
400 col = s->start_col;
401
402 if (c == -1 || (same_line && c == '\n'))
403 return NULL;
404 s->last_line = line;
405
406 if (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 c == '{' ||
421 c == '}') {
422 tok = isl_token_new(s->ctx, line, col, old_line != line);
423 if (!tok)
424 return NULL;
425 tok->type = (enum isl_token_type)c;
426 return tok;
427 }
428 if (c == '-') {
429 int c;
430 if ((c = isl_stream_getc(s)) == '>') {
431 tok = isl_token_new(s->ctx, line, col, old_line != line);
432 if (!tok)
433 return NULL;
434 tok->u.s = strdup("->");
435 tok->type = ISL_TOKEN_TO;
436 return tok;
437 }
438 if (c != -1)
439 isl_stream_ungetc(s, c);
440 if (!isdigit(c)) {
441 tok = isl_token_new(s->ctx, line, col, old_line != line);
442 if (!tok)
443 return NULL;
444 tok->type = (enum isl_token_type) '-';
445 return tok;
446 }
447 }
448 if (c == '-' || isdigit(c)) {
449 int minus = c == '-';
450 tok = isl_token_new(s->ctx, line, col, old_line != line);
451 if (!tok)
452 return NULL;
453 tok->type = ISL_TOKEN_VALUE;
454 isl_int_init(tok->u.v);
455 if (isl_stream_push_char(s, c))
456 goto error;
457 while ((c = isl_stream_getc(s)) != -1 && isdigit(c))
458 if (isl_stream_push_char(s, c))
459 goto error;
460 if (c != -1)
461 isl_stream_ungetc(s, c);
462 isl_stream_push_char(s, '\0');
463 isl_int_read(tok->u.v, s->buffer);
464 if (minus && isl_int_is_zero(tok->u.v)) {
465 tok->col++;
466 tok->on_new_line = 0;
467 isl_stream_push_token(s, tok);
468 tok = isl_token_new(s->ctx, line, col, old_line != line);
469 if (!tok)
470 return NULL;
471 tok->type = (enum isl_token_type) '-';
472 }
473 return tok;
474 }
475 if (isalpha(c) || c == '_') {
476 tok = isl_token_new(s->ctx, line, col, old_line != line);
477 if (!tok)
478 return NULL;
479 isl_stream_push_char(s, c);
480 while ((c = isl_stream_getc(s)) != -1 &&
481 (isalnum(c) || c == '_'))
482 isl_stream_push_char(s, c);
483 if (c != -1)
484 isl_stream_ungetc(s, c);
485 while ((c = isl_stream_getc(s)) != -1 && c == '\'')
486 isl_stream_push_char(s, c);
487 if (c != -1)
488 isl_stream_ungetc(s, c);
489 isl_stream_push_char(s, '\0');
490 tok->type = check_keywords(s);
491 if (tok->type != ISL_TOKEN_IDENT)
492 tok->is_keyword = 1;
493 tok->u.s = strdup(s->buffer);
494 if (!tok->u.s)
495 goto error;
496 return tok;
497 }
498 if (c == '"') {
499 tok = isl_token_new(s->ctx, line, col, old_line != line);
500 if (!tok)
501 return NULL;
502 tok->type = ISL_TOKEN_STRING;
503 tok->u.s = NULL;
504 while ((c = isl_stream_getc(s)) != -1 && c != '"' && c != '\n')
505 isl_stream_push_char(s, c);
506 if (c != '"') {
507 isl_stream_error(s, NULL, "unterminated string");
508 goto error;
509 }
510 isl_stream_push_char(s, '\0');
511 tok->u.s = strdup(s->buffer);
512 return tok;
513 }
514 if (c == '=') {
515 int c;
516 tok = isl_token_new(s->ctx, line, col, old_line != line);
517 if (!tok)
518 return NULL;
519 if ((c = isl_stream_getc(s)) == '=') {
520 tok->u.s = strdup("==");
521 tok->type = ISL_TOKEN_EQ_EQ;
522 return tok;
523 }
524 if (c != -1)
525 isl_stream_ungetc(s, c);
526 tok->type = (enum isl_token_type) '=';
527 return tok;
528 }
529 if (c == ':') {
530 int c;
531 tok = isl_token_new(s->ctx, line, col, old_line != line);
532 if (!tok)
533 return NULL;
534 if ((c = isl_stream_getc(s)) == '=') {
535 tok->u.s = strdup(":=");
536 tok->type = ISL_TOKEN_DEF;
537 return tok;
538 }
539 if (c != -1)
540 isl_stream_ungetc(s, c);
541 tok->type = (enum isl_token_type) ':';
542 return tok;
543 }
544 if (c == '>') {
545 int c;
546 tok = isl_token_new(s->ctx, line, col, old_line != line);
547 if (!tok)
548 return NULL;
549 if ((c = isl_stream_getc(s)) == '=') {
550 tok->u.s = strdup(">=");
551 tok->type = ISL_TOKEN_GE;
552 return tok;
553 } else if (c == '>') {
554 if ((c = isl_stream_getc(s)) == '=') {
555 tok->u.s = strdup(">>=");
556 tok->type = ISL_TOKEN_LEX_GE;
557 return tok;
558 }
559 tok->u.s = strdup(">>");
560 tok->type = ISL_TOKEN_LEX_GT;
561 } else {
562 tok->u.s = strdup(">");
563 tok->type = ISL_TOKEN_GT;
564 }
565 if (c != -1)
566 isl_stream_ungetc(s, c);
567 return tok;
568 }
569 if (c == '<') {
570 int c;
571 tok = isl_token_new(s->ctx, line, col, old_line != line);
572 if (!tok)
573 return NULL;
574 if ((c = isl_stream_getc(s)) == '=') {
575 tok->u.s = strdup("<=");
576 tok->type = ISL_TOKEN_LE;
577 return tok;
578 } else if (c == '<') {
579 if ((c = isl_stream_getc(s)) == '=') {
580 tok->u.s = strdup("<<=");
581 tok->type = ISL_TOKEN_LEX_LE;
582 return tok;
583 }
584 tok->u.s = strdup("<<");
585 tok->type = ISL_TOKEN_LEX_LT;
586 } else {
587 tok->u.s = strdup("<");
588 tok->type = ISL_TOKEN_LT;
589 }
590 if (c != -1)
591 isl_stream_ungetc(s, c);
592 return tok;
593 }
594 if (c == '&') {
595 tok = isl_token_new(s->ctx, line, col, old_line != line);
596 if (!tok)
597 return NULL;
598 tok->type = ISL_TOKEN_AND;
599 if ((c = isl_stream_getc(s)) != '&' && c != -1) {
600 tok->u.s = strdup("&");
601 isl_stream_ungetc(s, c);
602 } else
603 tok->u.s = strdup("&&");
604 return tok;
605 }
606 if (c == '|') {
607 tok = isl_token_new(s->ctx, line, col, old_line != line);
608 if (!tok)
609 return NULL;
610 tok->type = ISL_TOKEN_OR;
611 if ((c = isl_stream_getc(s)) != '|' && c != -1) {
612 tok->u.s = strdup("|");
613 isl_stream_ungetc(s, c);
614 } else
615 tok->u.s = strdup("||");
616 return tok;
617 }
618 if (c == '/') {
619 tok = isl_token_new(s->ctx, line, col, old_line != line);
620 if (!tok)
621 return NULL;
622 if ((c = isl_stream_getc(s)) == '\\') {
623 tok->u.s = strdup("/\\");
624 tok->type = ISL_TOKEN_AND;
625 return tok;
626 } else if (c == '/') {
627 tok->u.s = strdup("//");
628 tok->type = ISL_TOKEN_INT_DIV;
629 return tok;
630 } else {
631 tok->type = (enum isl_token_type) '/';
632 }
633 if (c != -1)
634 isl_stream_ungetc(s, c);
635 return tok;
636 }
637 if (c == '\\') {
638 tok = isl_token_new(s->ctx, line, col, old_line != line);
639 if (!tok)
640 return NULL;
641 if ((c = isl_stream_getc(s)) != '/' && c != -1) {
642 tok->type = (enum isl_token_type) '\\';
643 isl_stream_ungetc(s, c);
644 } else {
645 tok->u.s = strdup("\\/");
646 tok->type = ISL_TOKEN_OR;
647 }
648 return tok;
649 }
650 if (c == '!') {
651 tok = isl_token_new(s->ctx, line, col, old_line != line);
652 if (!tok)
653 return NULL;
654 if ((c = isl_stream_getc(s)) == '=') {
655 tok->u.s = strdup("!=");
656 tok->type = ISL_TOKEN_NE;
657 return tok;
658 } else {
659 tok->type = ISL_TOKEN_NOT;
660 tok->u.s = strdup("!");
661 }
662 if (c != -1)
663 isl_stream_ungetc(s, c);
664 return tok;
665 }
666
667 tok = isl_token_new(s->ctx, line, col, old_line != line);
668 if (!tok)
669 return NULL;
670 tok->type = ISL_TOKEN_UNKNOWN;
671 return tok;
672 error:
673 isl_token_free(tok);
674 return NULL;
675 }
676
isl_stream_next_token(__isl_keep isl_stream * s)677 struct isl_token *isl_stream_next_token(__isl_keep isl_stream *s)
678 {
679 return next_token(s, 0);
680 }
681
isl_stream_next_token_on_same_line(__isl_keep isl_stream * s)682 struct isl_token *isl_stream_next_token_on_same_line(__isl_keep isl_stream *s)
683 {
684 return next_token(s, 1);
685 }
686
isl_stream_eat_if_available(__isl_keep isl_stream * s,int type)687 int isl_stream_eat_if_available(__isl_keep isl_stream *s, int type)
688 {
689 struct isl_token *tok;
690
691 tok = isl_stream_next_token(s);
692 if (!tok)
693 return 0;
694 if (tok->type == type) {
695 isl_token_free(tok);
696 return 1;
697 }
698 isl_stream_push_token(s, tok);
699 return 0;
700 }
701
isl_stream_next_token_is(__isl_keep isl_stream * s,int type)702 int isl_stream_next_token_is(__isl_keep isl_stream *s, int type)
703 {
704 struct isl_token *tok;
705 int r;
706
707 tok = isl_stream_next_token(s);
708 if (!tok)
709 return 0;
710 r = tok->type == type;
711 isl_stream_push_token(s, tok);
712 return r;
713 }
714
isl_stream_read_ident_if_available(__isl_keep isl_stream * s)715 char *isl_stream_read_ident_if_available(__isl_keep isl_stream *s)
716 {
717 struct isl_token *tok;
718
719 tok = isl_stream_next_token(s);
720 if (!tok)
721 return NULL;
722 if (tok->type == ISL_TOKEN_IDENT) {
723 char *ident = strdup(tok->u.s);
724 isl_token_free(tok);
725 return ident;
726 }
727 isl_stream_push_token(s, tok);
728 return NULL;
729 }
730
isl_stream_eat(__isl_keep isl_stream * s,int type)731 int isl_stream_eat(__isl_keep isl_stream *s, int type)
732 {
733 struct isl_token *tok;
734
735 tok = isl_stream_next_token(s);
736 if (!tok) {
737 if (s->eof)
738 isl_stream_error(s, NULL, "unexpected EOF");
739 return -1;
740 }
741 if (tok->type == type) {
742 isl_token_free(tok);
743 return 0;
744 }
745 isl_stream_error(s, tok, "expecting other token");
746 isl_stream_push_token(s, tok);
747 return -1;
748 }
749
isl_stream_is_empty(__isl_keep isl_stream * s)750 int isl_stream_is_empty(__isl_keep isl_stream *s)
751 {
752 struct isl_token *tok;
753
754 tok = isl_stream_next_token(s);
755
756 if (!tok)
757 return 1;
758
759 isl_stream_push_token(s, tok);
760 return 0;
761 }
762
free_keyword(void ** p,void * user)763 static isl_stat free_keyword(void **p, void *user)
764 {
765 struct isl_keyword *keyword = *p;
766
767 free(keyword->name);
768 free(keyword);
769
770 return isl_stat_ok;
771 }
772
isl_stream_flush_tokens(__isl_keep isl_stream * s)773 void isl_stream_flush_tokens(__isl_keep isl_stream *s)
774 {
775 int i;
776
777 if (!s)
778 return;
779 for (i = 0; i < s->n_token; ++i)
780 isl_token_free(s->tokens[i]);
781 s->n_token = 0;
782 }
783
isl_stream_get_ctx(__isl_keep isl_stream * s)784 isl_ctx *isl_stream_get_ctx(__isl_keep isl_stream *s)
785 {
786 return s ? s->ctx : NULL;
787 }
788
isl_stream_free(__isl_take isl_stream * s)789 void isl_stream_free(__isl_take isl_stream *s)
790 {
791 if (!s)
792 return;
793 free(s->buffer);
794 if (s->n_token != 0) {
795 struct isl_token *tok = isl_stream_next_token(s);
796 isl_stream_error(s, tok, "unexpected token");
797 isl_token_free(tok);
798 }
799 if (s->keywords) {
800 isl_hash_table_foreach(s->ctx, s->keywords, &free_keyword, NULL);
801 isl_hash_table_free(s->ctx, s->keywords);
802 }
803 free(s->yaml_state);
804 free(s->yaml_indent);
805 isl_ctx_deref(s->ctx);
806 free(s);
807 }
808
809 /* Push "state" onto the stack of currently active YAML elements.
810 * The caller is responsible for setting the corresponding indentation.
811 * Return 0 on success and -1 on failure.
812 */
push_state(__isl_keep isl_stream * s,enum isl_yaml_state state)813 static int push_state(__isl_keep isl_stream *s, enum isl_yaml_state state)
814 {
815 if (s->yaml_size < s->yaml_depth + 1) {
816 int *indent;
817 enum isl_yaml_state *state;
818
819 state = isl_realloc_array(s->ctx, s->yaml_state,
820 enum isl_yaml_state, s->yaml_depth + 1);
821 if (!state)
822 return -1;
823 s->yaml_state = state;
824
825 indent = isl_realloc_array(s->ctx, s->yaml_indent,
826 int, s->yaml_depth + 1);
827 if (!indent)
828 return -1;
829 s->yaml_indent = indent;
830
831 s->yaml_size = s->yaml_depth + 1;
832 }
833
834 s->yaml_state[s->yaml_depth] = state;
835 s->yaml_depth++;
836
837 return 0;
838 }
839
840 /* Remove the innermost active YAML element from the stack.
841 * Return 0 on success and -1 on failure.
842 */
pop_state(__isl_keep isl_stream * s)843 static int pop_state(__isl_keep isl_stream *s)
844 {
845 if (!s)
846 return -1;
847 if (s->yaml_depth < 1)
848 isl_die(isl_stream_get_ctx(s), isl_error_invalid,
849 "not in YAML construct", return -1);
850
851 s->yaml_depth--;
852
853 return 0;
854 }
855
856 /* Set the state of the innermost active YAML element to "state".
857 * Return 0 on success and -1 on failure.
858 */
update_state(__isl_keep isl_stream * s,enum isl_yaml_state state)859 static int update_state(__isl_keep isl_stream *s, enum isl_yaml_state state)
860 {
861 if (!s)
862 return -1;
863 if (s->yaml_depth < 1)
864 isl_die(isl_stream_get_ctx(s), isl_error_invalid,
865 "not in YAML construct", return -1);
866
867 s->yaml_state[s->yaml_depth - 1] = state;
868
869 return 0;
870 }
871
872 /* Return the state of the innermost active YAML element.
873 * Return isl_yaml_none if we are not inside any YAML element.
874 */
current_state(__isl_keep isl_stream * s)875 static enum isl_yaml_state current_state(__isl_keep isl_stream *s)
876 {
877 if (!s)
878 return isl_yaml_none;
879 if (s->yaml_depth < 1)
880 return isl_yaml_none;
881 return s->yaml_state[s->yaml_depth - 1];
882 }
883
884 /* Set the indentation of the innermost active YAML element to "indent".
885 * If "indent" is equal to ISL_YAML_INDENT_FLOW, then this means
886 * that the current elemient is in flow format.
887 */
set_yaml_indent(__isl_keep isl_stream * s,int indent)888 static int set_yaml_indent(__isl_keep isl_stream *s, int indent)
889 {
890 if (s->yaml_depth < 1)
891 isl_die(s->ctx, isl_error_internal,
892 "not in YAML element", return -1);
893
894 s->yaml_indent[s->yaml_depth - 1] = indent;
895
896 return 0;
897 }
898
899 /* Return the indentation of the innermost active YAML element
900 * of -1 on error.
901 */
get_yaml_indent(__isl_keep isl_stream * s)902 static int get_yaml_indent(__isl_keep isl_stream *s)
903 {
904 if (s->yaml_depth < 1)
905 isl_die(s->ctx, isl_error_internal,
906 "not in YAML element", return -1);
907
908 return s->yaml_indent[s->yaml_depth - 1];
909 }
910
911 /* Move to the next state at the innermost level.
912 * Return 1 if successful.
913 * Return 0 if we are at the end of the innermost level.
914 * Return -1 on error.
915 *
916 * If we are in state isl_yaml_mapping_key_start, then we have just
917 * started a mapping and we are expecting a key. If the mapping started
918 * with a '{', then we check if the next token is a '}'. If so,
919 * then the mapping is empty and there is no next state at this level.
920 * Otherwise, we assume that there is at least one key (the one from
921 * which we derived the indentation in isl_stream_yaml_read_start_mapping.
922 *
923 * If we are in state isl_yaml_mapping_key, then the we expect a colon
924 * followed by a value, so there is always a next state unless
925 * some error occurs.
926 *
927 * If we are in state isl_yaml_mapping_val, then there may or may
928 * not be a subsequent key in the same mapping.
929 * In flow format, the next key is preceded by a comma.
930 * In block format, the next key has the same indentation as the first key.
931 * If the first token has a smaller indentation, then we have reached
932 * the end of the current mapping.
933 *
934 * If we are in state isl_yaml_sequence_start, then we have just
935 * started a sequence. If the sequence started with a '[',
936 * then we check if the next token is a ']'. If so, then the sequence
937 * is empty and there is no next state at this level.
938 * Otherwise, we assume that there is at least one element in the sequence
939 * (the one from which we derived the indentation in
940 * isl_stream_yaml_read_start_sequence.
941 *
942 * If we are in state isl_yaml_sequence, then there may or may
943 * not be a subsequent element in the same sequence.
944 * In flow format, the next element is preceded by a comma.
945 * In block format, the next element is introduced by a dash with
946 * the same indentation as that of the first element.
947 * If the first token is not a dash or if it has a smaller indentation,
948 * then we have reached the end of the current sequence.
949 */
isl_stream_yaml_next(__isl_keep isl_stream * s)950 int isl_stream_yaml_next(__isl_keep isl_stream *s)
951 {
952 struct isl_token *tok;
953 enum isl_yaml_state state;
954 int indent;
955
956 state = current_state(s);
957 if (state == isl_yaml_none)
958 isl_die(s->ctx, isl_error_invalid,
959 "not in YAML element", return -1);
960 switch (state) {
961 case isl_yaml_mapping_key_start:
962 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW &&
963 isl_stream_next_token_is(s, '}'))
964 return 0;
965 if (update_state(s, isl_yaml_mapping_key) < 0)
966 return -1;
967 return 1;
968 case isl_yaml_mapping_key:
969 tok = isl_stream_next_token(s);
970 if (!tok) {
971 if (s->eof)
972 isl_stream_error(s, NULL, "unexpected EOF");
973 return -1;
974 }
975 if (tok->type == ':') {
976 isl_token_free(tok);
977 if (update_state(s, isl_yaml_mapping_val) < 0)
978 return -1;
979 return 1;
980 }
981 isl_stream_error(s, tok, "expecting ':'");
982 isl_stream_push_token(s, tok);
983 return -1;
984 case isl_yaml_mapping_val:
985 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) {
986 if (!isl_stream_eat_if_available(s, ','))
987 return 0;
988 if (update_state(s, isl_yaml_mapping_key) < 0)
989 return -1;
990 return 1;
991 }
992 tok = isl_stream_next_token(s);
993 if (!tok)
994 return 0;
995 indent = tok->col - 1;
996 isl_stream_push_token(s, tok);
997 if (indent < get_yaml_indent(s))
998 return 0;
999 if (update_state(s, isl_yaml_mapping_key) < 0)
1000 return -1;
1001 return 1;
1002 case isl_yaml_sequence_start:
1003 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) {
1004 if (isl_stream_next_token_is(s, ']'))
1005 return 0;
1006 if (update_state(s, isl_yaml_sequence) < 0)
1007 return -1;
1008 return 1;
1009 }
1010 tok = isl_stream_next_token(s);
1011 if (!tok) {
1012 if (s->eof)
1013 isl_stream_error(s, NULL, "unexpected EOF");
1014 return -1;
1015 }
1016 if (tok->type == '-') {
1017 isl_token_free(tok);
1018 if (update_state(s, isl_yaml_sequence) < 0)
1019 return -1;
1020 return 1;
1021 }
1022 isl_stream_error(s, tok, "expecting '-'");
1023 isl_stream_push_token(s, tok);
1024 return 0;
1025 case isl_yaml_sequence:
1026 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW)
1027 return isl_stream_eat_if_available(s, ',');
1028 tok = isl_stream_next_token(s);
1029 if (!tok)
1030 return 0;
1031 indent = tok->col - 1;
1032 if (indent < get_yaml_indent(s) || tok->type != '-') {
1033 isl_stream_push_token(s, tok);
1034 return 0;
1035 }
1036 isl_token_free(tok);
1037 return 1;
1038 default:
1039 isl_die(s->ctx, isl_error_internal,
1040 "unexpected state", return 0);
1041 }
1042 }
1043
1044 /* Start reading a YAML mapping.
1045 * Return 0 on success and -1 on error.
1046 *
1047 * If the first token on the stream is a '{' then we remove this token
1048 * from the stream and keep track of the fact that the mapping
1049 * is given in flow format.
1050 * Otherwise, we assume the first token is the first key of the mapping and
1051 * keep track of its indentation, but keep the token on the stream.
1052 * In both cases, the next token we expect is the first key of the mapping.
1053 */
isl_stream_yaml_read_start_mapping(__isl_keep isl_stream * s)1054 int isl_stream_yaml_read_start_mapping(__isl_keep isl_stream *s)
1055 {
1056 struct isl_token *tok;
1057 int indent;
1058
1059 if (push_state(s, isl_yaml_mapping_key_start) < 0)
1060 return -1;
1061
1062 tok = isl_stream_next_token(s);
1063 if (!tok) {
1064 if (s->eof)
1065 isl_stream_error(s, NULL, "unexpected EOF");
1066 return -1;
1067 }
1068 if (isl_token_get_type(tok) == '{') {
1069 isl_token_free(tok);
1070 return set_yaml_indent(s, ISL_YAML_INDENT_FLOW);
1071 }
1072 indent = tok->col - 1;
1073 isl_stream_push_token(s, tok);
1074
1075 return set_yaml_indent(s, indent);
1076 }
1077
1078 /* Finish reading a YAML mapping.
1079 * Return 0 on success and -1 on error.
1080 *
1081 * If the mapping started with a '{', then we expect a '}' to close
1082 * the mapping.
1083 * Otherwise, we double-check that the next token (if any)
1084 * has a smaller indentation than that of the current mapping.
1085 */
isl_stream_yaml_read_end_mapping(__isl_keep isl_stream * s)1086 int isl_stream_yaml_read_end_mapping(__isl_keep isl_stream *s)
1087 {
1088 struct isl_token *tok;
1089 int indent;
1090
1091 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) {
1092 if (isl_stream_eat(s, '}') < 0)
1093 return -1;
1094 return pop_state(s);
1095 }
1096
1097 tok = isl_stream_next_token(s);
1098 if (!tok)
1099 return pop_state(s);
1100
1101 indent = tok->col - 1;
1102 isl_stream_push_token(s, tok);
1103
1104 if (indent >= get_yaml_indent(s))
1105 isl_die(isl_stream_get_ctx(s), isl_error_invalid,
1106 "mapping not finished", return -1);
1107
1108 return pop_state(s);
1109 }
1110
1111 /* Start reading a YAML sequence.
1112 * Return 0 on success and -1 on error.
1113 *
1114 * If the first token on the stream is a '[' then we remove this token
1115 * from the stream and keep track of the fact that the sequence
1116 * is given in flow format.
1117 * Otherwise, we assume the first token is the dash that introduces
1118 * the first element of the sequence and keep track of its indentation,
1119 * but keep the token on the stream.
1120 * In both cases, the next token we expect is the first element
1121 * of the sequence.
1122 */
isl_stream_yaml_read_start_sequence(__isl_keep isl_stream * s)1123 int isl_stream_yaml_read_start_sequence(__isl_keep isl_stream *s)
1124 {
1125 struct isl_token *tok;
1126 int indent;
1127
1128 if (push_state(s, isl_yaml_sequence_start) < 0)
1129 return -1;
1130
1131 tok = isl_stream_next_token(s);
1132 if (!tok) {
1133 if (s->eof)
1134 isl_stream_error(s, NULL, "unexpected EOF");
1135 return -1;
1136 }
1137 if (isl_token_get_type(tok) == '[') {
1138 isl_token_free(tok);
1139 return set_yaml_indent(s, ISL_YAML_INDENT_FLOW);
1140 }
1141 indent = tok->col - 1;
1142 isl_stream_push_token(s, tok);
1143
1144 return set_yaml_indent(s, indent);
1145 }
1146
1147 /* Finish reading a YAML sequence.
1148 * Return 0 on success and -1 on error.
1149 *
1150 * If the sequence started with a '[', then we expect a ']' to close
1151 * the sequence.
1152 * Otherwise, we double-check that the next token (if any)
1153 * is not a dash or that it has a smaller indentation than
1154 * that of the current sequence.
1155 */
isl_stream_yaml_read_end_sequence(__isl_keep isl_stream * s)1156 int isl_stream_yaml_read_end_sequence(__isl_keep isl_stream *s)
1157 {
1158 struct isl_token *tok;
1159 int indent;
1160 int dash;
1161
1162 if (get_yaml_indent(s) == ISL_YAML_INDENT_FLOW) {
1163 if (isl_stream_eat(s, ']') < 0)
1164 return -1;
1165 return pop_state(s);
1166 }
1167
1168 tok = isl_stream_next_token(s);
1169 if (!tok)
1170 return pop_state(s);
1171
1172 indent = tok->col - 1;
1173 dash = tok->type == '-';
1174 isl_stream_push_token(s, tok);
1175
1176 if (indent >= get_yaml_indent(s) && dash)
1177 isl_die(isl_stream_get_ctx(s), isl_error_invalid,
1178 "sequence not finished", return -1);
1179
1180 return pop_state(s);
1181 }
1182