add isl_aff_mod_val
[platform/upstream/isl.git] / isl_stream.c
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 <strings.h>
13 #include <isl/ctx.h>
14 #include <isl_stream_private.h>
15 #include <isl/map.h>
16 #include <isl/aff.h>
17
18 struct isl_keyword {
19         char                    *name;
20         enum isl_token_type     type;
21 };
22
23 static int same_name(const void *entry, const void *val)
24 {
25         const struct isl_keyword *keyword = (const struct isl_keyword *)entry;
26
27         return !strcmp(keyword->name, val);
28 }
29
30 enum isl_token_type isl_stream_register_keyword(struct 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
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 void isl_token_free(struct isl_token *tok)
84 {
85         if (!tok)
86                 return;
87         if (tok->type == ISL_TOKEN_VALUE)
88                 isl_int_clear(tok->u.v);
89         else if (tok->type == ISL_TOKEN_MAP)
90                 isl_map_free(tok->u.map);
91         else if (tok->type == ISL_TOKEN_AFF)
92                 isl_pw_aff_free(tok->u.pwaff);
93         else
94                 free(tok->u.s);
95         free(tok);
96 }
97
98 void isl_stream_error(struct isl_stream *s, struct isl_token *tok, char *msg)
99 {
100         int line = tok ? tok->line : s->line;
101         int col = tok ? tok->col : s->col;
102         fprintf(stderr, "syntax error (%d, %d): %s\n", line, col, msg);
103         if (tok) {
104                 if (tok->type < 256)
105                         fprintf(stderr, "got '%c'\n", tok->type);
106                 else if (tok->type == ISL_TOKEN_IDENT)
107                         fprintf(stderr, "got ident '%s'\n", tok->u.s);
108                 else if (tok->is_keyword)
109                         fprintf(stderr, "got keyword '%s'\n", tok->u.s);
110                 else if (tok->type == ISL_TOKEN_VALUE) {
111                         fprintf(stderr, "got value '");
112                         isl_int_print(stderr, tok->u.v, 0);
113                         fprintf(stderr, "'\n");
114                 } else if (tok->type == ISL_TOKEN_MAP) {
115                         isl_printer *p;
116                         fprintf(stderr, "got map '");
117                         p = isl_printer_to_file(s->ctx, stderr);
118                         p = isl_printer_print_map(p, tok->u.map);
119                         isl_printer_free(p);
120                         fprintf(stderr, "'\n");
121                 } else if (tok->type == ISL_TOKEN_AFF) {
122                         isl_printer *p;
123                         fprintf(stderr, "got affine expression '");
124                         p = isl_printer_to_file(s->ctx, stderr);
125                         p = isl_printer_print_pw_aff(p, tok->u.pwaff);
126                         isl_printer_free(p);
127                         fprintf(stderr, "'\n");
128                 } else if (tok->u.s)
129                         fprintf(stderr, "got token '%s'\n", tok->u.s);
130                 else
131                         fprintf(stderr, "got token type %d\n", tok->type);
132         }
133 }
134
135 static struct isl_stream* isl_stream_new(struct isl_ctx *ctx)
136 {
137         int i;
138         struct isl_stream *s = isl_alloc_type(ctx, struct isl_stream);
139         if (!s)
140                 return NULL;
141         s->ctx = ctx;
142         isl_ctx_ref(s->ctx);
143         s->file = NULL;
144         s->str = NULL;
145         s->len = 0;
146         s->line = 1;
147         s->col = 0;
148         s->eof = 0;
149         s->c = -1;
150         s->n_un = 0;
151         for (i = 0; i < 5; ++i)
152                 s->tokens[i] = NULL;
153         s->n_token = 0;
154         s->keywords = NULL;
155         s->size = 256;
156         s->buffer = isl_alloc_array(ctx, char, s->size);
157         if (!s->buffer)
158                 goto error;
159         return s;
160 error:
161         isl_stream_free(s);
162         return NULL;
163 }
164
165 struct isl_stream* isl_stream_new_file(struct isl_ctx *ctx, FILE *file)
166 {
167         struct isl_stream *s = isl_stream_new(ctx);
168         if (!s)
169                 return NULL;
170         s->file = file;
171         return s;
172 }
173
174 struct isl_stream* isl_stream_new_str(struct isl_ctx *ctx, const char *str)
175 {
176         struct isl_stream *s;
177         if (!str)
178                 return NULL;
179         s = isl_stream_new(ctx);
180         if (!s)
181                 return NULL;
182         s->str = str;
183         return s;
184 }
185
186 static int stream_getc(struct isl_stream *s)
187 {
188         int c;
189         if (s->eof)
190                 return -1;
191         if (s->n_un)
192                 return s->c = s->un[--s->n_un];
193         if (s->file)
194                 c = fgetc(s->file);
195         else {
196                 c = *s->str++;
197                 if (c == '\0')
198                         c = -1;
199         }
200         if (c == -1)
201                 s->eof = 1;
202         if (!s->eof) {
203                 if (s->c == '\n') {
204                         s->line++;
205                         s->col = 0;
206                 } else
207                         s->col++;
208         }
209         s->c = c;
210         return c;
211 }
212
213 static void isl_stream_ungetc(struct isl_stream *s, int c)
214 {
215         isl_assert(s->ctx, s->n_un < 5, return);
216         s->un[s->n_un++] = c;
217         s->c = -1;
218 }
219
220 static int isl_stream_getc(struct isl_stream *s)
221 {
222         int c;
223
224         do {
225                 c = stream_getc(s);
226                 if (c != '\\')
227                         return c;
228                 c = stream_getc(s);
229         } while (c == '\n');
230
231         isl_stream_ungetc(s, c);
232
233         return '\\';
234 }
235
236 static int isl_stream_push_char(struct isl_stream *s, int c)
237 {
238         if (s->len >= s->size) {
239                 char *buffer;
240                 s->size = (3*s->size)/2;
241                 buffer = isl_realloc_array(s->ctx, s->buffer, char, s->size);
242                 if (!buffer)
243                         return -1;
244                 s->buffer = buffer;
245         }
246         s->buffer[s->len++] = c;
247         return 0;
248 }
249
250 void isl_stream_push_token(struct isl_stream *s, struct isl_token *tok)
251 {
252         isl_assert(s->ctx, s->n_token < 5, return);
253         s->tokens[s->n_token++] = tok;
254 }
255
256 static enum isl_token_type check_keywords(struct isl_stream *s)
257 {
258         struct isl_hash_table_entry *entry;
259         struct isl_keyword *keyword;
260         uint32_t name_hash;
261
262         if (!strcasecmp(s->buffer, "exists"))
263                 return ISL_TOKEN_EXISTS;
264         if (!strcasecmp(s->buffer, "and"))
265                 return ISL_TOKEN_AND;
266         if (!strcasecmp(s->buffer, "or"))
267                 return ISL_TOKEN_OR;
268         if (!strcasecmp(s->buffer, "not"))
269                 return ISL_TOKEN_NOT;
270         if (!strcasecmp(s->buffer, "infty"))
271                 return ISL_TOKEN_INFTY;
272         if (!strcasecmp(s->buffer, "infinity"))
273                 return ISL_TOKEN_INFTY;
274         if (!strcasecmp(s->buffer, "NaN"))
275                 return ISL_TOKEN_NAN;
276         if (!strcasecmp(s->buffer, "min"))
277                 return ISL_TOKEN_MIN;
278         if (!strcasecmp(s->buffer, "max"))
279                 return ISL_TOKEN_MAX;
280         if (!strcasecmp(s->buffer, "rat"))
281                 return ISL_TOKEN_RAT;
282         if (!strcasecmp(s->buffer, "true"))
283                 return ISL_TOKEN_TRUE;
284         if (!strcasecmp(s->buffer, "false"))
285                 return ISL_TOKEN_FALSE;
286         if (!strcasecmp(s->buffer, "ceild"))
287                 return ISL_TOKEN_CEILD;
288         if (!strcasecmp(s->buffer, "floord"))
289                 return ISL_TOKEN_FLOORD;
290         if (!strcasecmp(s->buffer, "mod"))
291                 return ISL_TOKEN_MOD;
292
293         if (!s->keywords)
294                 return ISL_TOKEN_IDENT;
295
296         name_hash = isl_hash_string(isl_hash_init(), s->buffer);
297         entry = isl_hash_table_find(s->ctx, s->keywords, name_hash, same_name,
298                                         s->buffer, 0);
299         if (entry) {
300                 keyword = entry->data;
301                 return keyword->type;
302         }
303
304         return ISL_TOKEN_IDENT;
305 }
306
307 int isl_stream_skip_line(struct isl_stream *s)
308 {
309         int c;
310
311         while ((c = isl_stream_getc(s)) != -1 && c != '\n')
312                 /* nothing */
313                 ;
314
315         return c == -1 ? -1 : 0;
316 }
317
318 static struct isl_token *next_token(struct isl_stream *s, int same_line)
319 {
320         int c;
321         struct isl_token *tok = NULL;
322         int line, col;
323         int old_line = s->line;
324
325         if (s->n_token) {
326                 if (same_line && s->tokens[s->n_token - 1]->on_new_line)
327                         return NULL;
328                 return s->tokens[--s->n_token];
329         }
330
331         if (same_line && s->c == '\n')
332                 return NULL;
333
334         s->len = 0;
335
336         /* skip spaces and comment lines */
337         while ((c = isl_stream_getc(s)) != -1) {
338                 if (c == '#') {
339                         if (isl_stream_skip_line(s) < 0)
340                                 break;
341                         c = '\n';
342                         if (same_line)
343                                 break;
344                 } else if (!isspace(c) || (same_line && c == '\n'))
345                         break;
346         }
347
348         line = s->line;
349         col = s->col;
350
351         if (c == -1 || (same_line && c == '\n'))
352                 return NULL;
353         if (c == '(' ||
354             c == ')' ||
355             c == '+' ||
356             c == '*' ||
357             c == '%' ||
358             c == '?' ||
359             c == '^' ||
360             c == '@' ||
361             c == '$' ||
362             c == ',' ||
363             c == '.' ||
364             c == ';' ||
365             c == '[' ||
366             c == ']' ||
367             c == '{' ||
368             c == '}') {
369                 tok = isl_token_new(s->ctx, line, col, old_line != line);
370                 if (!tok)
371                         return NULL;
372                 tok->type = (enum isl_token_type)c;
373                 return tok;
374         }
375         if (c == '-') {
376                 int c;
377                 if ((c = isl_stream_getc(s)) == '>') {
378                         tok = isl_token_new(s->ctx, line, col, old_line != line);
379                         if (!tok)
380                                 return NULL;
381                         tok->u.s = strdup("->");
382                         tok->type = ISL_TOKEN_TO;
383                         return tok;
384                 }
385                 if (c != -1)
386                         isl_stream_ungetc(s, c);
387                 if (!isdigit(c)) {
388                         tok = isl_token_new(s->ctx, line, col, old_line != line);
389                         if (!tok)
390                                 return NULL;
391                         tok->type = (enum isl_token_type) '-';
392                         return tok;
393                 }
394         }
395         if (c == '-' || isdigit(c)) {
396                 int minus = c == '-';
397                 tok = isl_token_new(s->ctx, line, col, old_line != line);
398                 if (!tok)
399                         return NULL;
400                 tok->type = ISL_TOKEN_VALUE;
401                 isl_int_init(tok->u.v);
402                 if (isl_stream_push_char(s, c))
403                         goto error;
404                 while ((c = isl_stream_getc(s)) != -1 && isdigit(c))
405                         if (isl_stream_push_char(s, c))
406                                 goto error;
407                 if (c != -1)
408                         isl_stream_ungetc(s, c);
409                 isl_stream_push_char(s, '\0');
410                 isl_int_read(tok->u.v, s->buffer);
411                 if (minus && isl_int_is_zero(tok->u.v)) {
412                         tok->col++;
413                         tok->on_new_line = 0;
414                         isl_stream_push_token(s, tok);
415                         tok = isl_token_new(s->ctx, line, col, old_line != line);
416                         if (!tok)
417                                 return NULL;
418                         tok->type = (enum isl_token_type) '-';
419                 }
420                 return tok;
421         }
422         if (isalpha(c) || c == '_') {
423                 tok = isl_token_new(s->ctx, line, col, old_line != line);
424                 if (!tok)
425                         return NULL;
426                 isl_stream_push_char(s, c);
427                 while ((c = isl_stream_getc(s)) != -1 &&
428                                 (isalnum(c) || c == '_'))
429                         isl_stream_push_char(s, c);
430                 if (c != -1)
431                         isl_stream_ungetc(s, c);
432                 while ((c = isl_stream_getc(s)) != -1 && c == '\'')
433                         isl_stream_push_char(s, c);
434                 if (c != -1)
435                         isl_stream_ungetc(s, c);
436                 isl_stream_push_char(s, '\0');
437                 tok->type = check_keywords(s);
438                 if (tok->type != ISL_TOKEN_IDENT)
439                         tok->is_keyword = 1;
440                 tok->u.s = strdup(s->buffer);
441                 if (!tok->u.s)
442                         goto error;
443                 return tok;
444         }
445         if (c == '"') {
446                 tok = isl_token_new(s->ctx, line, col, old_line != line);
447                 if (!tok)
448                         return NULL;
449                 tok->type = ISL_TOKEN_STRING;
450                 tok->u.s = NULL;
451                 while ((c = isl_stream_getc(s)) != -1 && c != '"' && c != '\n')
452                         isl_stream_push_char(s, c);
453                 if (c != '"') {
454                         isl_stream_error(s, NULL, "unterminated string");
455                         goto error;
456                 }
457                 isl_stream_push_char(s, '\0');
458                 tok->u.s = strdup(s->buffer);
459                 return tok;
460         }
461         if (c == '=') {
462                 int c;
463                 tok = isl_token_new(s->ctx, line, col, old_line != line);
464                 if (!tok)
465                         return NULL;
466                 if ((c = isl_stream_getc(s)) == '=') {
467                         tok->u.s = strdup("==");
468                         tok->type = ISL_TOKEN_EQ_EQ;
469                         return tok;
470                 }
471                 if (c != -1)
472                         isl_stream_ungetc(s, c);
473                 tok->type = (enum isl_token_type) '=';
474                 return tok;
475         }
476         if (c == ':') {
477                 int c;
478                 tok = isl_token_new(s->ctx, line, col, old_line != line);
479                 if (!tok)
480                         return NULL;
481                 if ((c = isl_stream_getc(s)) == '=') {
482                         tok->u.s = strdup(":=");
483                         tok->type = ISL_TOKEN_DEF;
484                         return tok;
485                 }
486                 if (c != -1)
487                         isl_stream_ungetc(s, c);
488                 tok->type = (enum isl_token_type) ':';
489                 return tok;
490         }
491         if (c == '>') {
492                 int c;
493                 tok = isl_token_new(s->ctx, line, col, old_line != line);
494                 if (!tok)
495                         return NULL;
496                 if ((c = isl_stream_getc(s)) == '=') {
497                         tok->u.s = strdup(">=");
498                         tok->type = ISL_TOKEN_GE;
499                         return tok;
500                 } else if (c == '>') {
501                         if ((c = isl_stream_getc(s)) == '=') {
502                                 tok->u.s = strdup(">>=");
503                                 tok->type = ISL_TOKEN_LEX_GE;
504                                 return tok;
505                         }
506                         tok->u.s = strdup(">>");
507                         tok->type = ISL_TOKEN_LEX_GT;
508                 } else {
509                         tok->u.s = strdup(">");
510                         tok->type = ISL_TOKEN_GT;
511                 }
512                 if (c != -1)
513                         isl_stream_ungetc(s, c);
514                 return tok;
515         }
516         if (c == '<') {
517                 int c;
518                 tok = isl_token_new(s->ctx, line, col, old_line != line);
519                 if (!tok)
520                         return NULL;
521                 if ((c = isl_stream_getc(s)) == '=') {
522                         tok->u.s = strdup("<=");
523                         tok->type = ISL_TOKEN_LE;
524                         return tok;
525                 } else if (c == '<') {
526                         if ((c = isl_stream_getc(s)) == '=') {
527                                 tok->u.s = strdup("<<=");
528                                 tok->type = ISL_TOKEN_LEX_LE;
529                                 return tok;
530                         }
531                         tok->u.s = strdup("<<");
532                         tok->type = ISL_TOKEN_LEX_LT;
533                 } else {
534                         tok->u.s = strdup("<");
535                         tok->type = ISL_TOKEN_LT;
536                 }
537                 if (c != -1)
538                         isl_stream_ungetc(s, c);
539                 return tok;
540         }
541         if (c == '&') {
542                 tok = isl_token_new(s->ctx, line, col, old_line != line);
543                 if (!tok)
544                         return NULL;
545                 tok->type = ISL_TOKEN_AND;
546                 if ((c = isl_stream_getc(s)) != '&' && c != -1) {
547                         tok->u.s = strdup("&");
548                         isl_stream_ungetc(s, c);
549                 } else
550                         tok->u.s = strdup("&&");
551                 return tok;
552         }
553         if (c == '|') {
554                 tok = isl_token_new(s->ctx, line, col, old_line != line);
555                 if (!tok)
556                         return NULL;
557                 tok->type = ISL_TOKEN_OR;
558                 if ((c = isl_stream_getc(s)) != '|' && c != -1) {
559                         tok->u.s = strdup("|");
560                         isl_stream_ungetc(s, c);
561                 } else
562                         tok->u.s = strdup("||");
563                 return tok;
564         }
565         if (c == '/') {
566                 tok = isl_token_new(s->ctx, line, col, old_line != line);
567                 if (!tok)
568                         return NULL;
569                 if ((c = isl_stream_getc(s)) != '\\' && c != -1) {
570                         tok->type = (enum isl_token_type) '/';
571                         isl_stream_ungetc(s, c);
572                 } else {
573                         tok->u.s = strdup("/\\");
574                         tok->type = ISL_TOKEN_AND;
575                 }
576                 return tok;
577         }
578         if (c == '\\') {
579                 tok = isl_token_new(s->ctx, line, col, old_line != line);
580                 if (!tok)
581                         return NULL;
582                 if ((c = isl_stream_getc(s)) != '/' && c != -1) {
583                         tok->type = (enum isl_token_type) '\\';
584                         isl_stream_ungetc(s, c);
585                 } else {
586                         tok->u.s = strdup("\\/");
587                         tok->type = ISL_TOKEN_OR;
588                 }
589                 return tok;
590         }
591         if (c == '!') {
592                 tok = isl_token_new(s->ctx, line, col, old_line != line);
593                 if (!tok)
594                         return NULL;
595                 if ((c = isl_stream_getc(s)) == '=') {
596                         tok->u.s = strdup("!=");
597                         tok->type = ISL_TOKEN_NE;
598                         return tok;
599                 } else {
600                         tok->type = ISL_TOKEN_NOT;
601                         tok->u.s = strdup("!");
602                 }
603                 if (c != -1)
604                         isl_stream_ungetc(s, c);
605                 return tok;
606         }
607
608         tok = isl_token_new(s->ctx, line, col, old_line != line);
609         if (!tok)
610                 return NULL;
611         tok->type = ISL_TOKEN_UNKNOWN;
612         return tok;
613 error:
614         isl_token_free(tok);
615         return NULL;
616 }
617
618 struct isl_token *isl_stream_next_token(struct isl_stream *s)
619 {
620         return next_token(s, 0);
621 }
622
623 struct isl_token *isl_stream_next_token_on_same_line(struct isl_stream *s)
624 {
625         return next_token(s, 1);
626 }
627
628 int isl_stream_eat_if_available(struct isl_stream *s, int type)
629 {
630         struct isl_token *tok;
631
632         tok = isl_stream_next_token(s);
633         if (!tok)
634                 return 0;
635         if (tok->type == type) {
636                 isl_token_free(tok);
637                 return 1;
638         }
639         isl_stream_push_token(s, tok);
640         return 0;
641 }
642
643 int isl_stream_next_token_is(struct isl_stream *s, int type)
644 {
645         struct isl_token *tok;
646         int r;
647
648         tok = isl_stream_next_token(s);
649         if (!tok)
650                 return 0;
651         r = tok->type == type;
652         isl_stream_push_token(s, tok);
653         return r;
654 }
655
656 char *isl_stream_read_ident_if_available(struct isl_stream *s)
657 {
658         struct isl_token *tok;
659
660         tok = isl_stream_next_token(s);
661         if (!tok)
662                 return NULL;
663         if (tok->type == ISL_TOKEN_IDENT) {
664                 char *ident = strdup(tok->u.s);
665                 isl_token_free(tok);
666                 return ident;
667         }
668         isl_stream_push_token(s, tok);
669         return NULL;
670 }
671
672 int isl_stream_eat(struct isl_stream *s, int type)
673 {
674         struct isl_token *tok;
675
676         tok = isl_stream_next_token(s);
677         if (!tok)
678                 return -1;
679         if (tok->type == type) {
680                 isl_token_free(tok);
681                 return 0;
682         }
683         isl_stream_error(s, tok, "expecting other token");
684         isl_stream_push_token(s, tok);
685         return -1;
686 }
687
688 int isl_stream_is_empty(struct isl_stream *s)
689 {
690         struct isl_token *tok;
691
692         tok = isl_stream_next_token(s);
693
694         if (!tok)
695                 return 1;
696
697         isl_stream_push_token(s, tok);
698         return 0;
699 }
700
701 static int free_keyword(void **p, void *user)
702 {
703         struct isl_keyword *keyword = *p;
704
705         free(keyword->name);
706         free(keyword);
707
708         return 0;
709 }
710
711 void isl_stream_flush_tokens(struct isl_stream *s)
712 {
713         int i;
714
715         if (!s)
716                 return;
717         for (i = 0; i < s->n_token; ++i)
718                 isl_token_free(s->tokens[i]);
719         s->n_token = 0;
720 }
721
722 void isl_stream_free(struct isl_stream *s)
723 {
724         if (!s)
725                 return;
726         free(s->buffer);
727         if (s->n_token != 0) {
728                 struct isl_token *tok = isl_stream_next_token(s);
729                 isl_stream_error(s, tok, "unexpected token");
730                 isl_token_free(tok);
731         }
732         if (s->keywords) {
733                 isl_hash_table_foreach(s->ctx, s->keywords, &free_keyword, NULL);
734                 isl_hash_table_free(s->ctx, s->keywords);
735         }
736         isl_ctx_deref(s->ctx);
737         free(s);
738 }