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