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