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