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 | |
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(b: !strcmp(s1: keyword->name, s2: val)); |
29 | } |
30 | |
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(ctx: s->ctx, min_size: 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(), s: name); |
46 | |
47 | entry = isl_hash_table_find(ctx: s->ctx, table: s->keywords, key_hash: name_hash, |
48 | eq: same_name, val: name, reserve: 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(s: name); |
61 | if (!keyword->name) { |
62 | free(ptr: keyword); |
63 | return ISL_TOKEN_ERROR; |
64 | } |
65 | entry->data = keyword; |
66 | |
67 | return keyword->type; |
68 | } |
69 | |
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 | */ |
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 | */ |
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, n: tok->u.v); |
102 | } |
103 | |
104 | /* Does the given token have a string representation? |
105 | */ |
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(b: tok->u.s != NULL); |
111 | } |
112 | |
113 | /* Given a token with a string representation, return a copy of this string. |
114 | */ |
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(s: tok->u.s); |
125 | } |
126 | |
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(map: tok->u.map); |
135 | else if (tok->type == ISL_TOKEN_AFF) |
136 | isl_pw_aff_free(pwaff: tok->u.pwaff); |
137 | else |
138 | free(ptr: tok->u.s); |
139 | free(ptr: tok); |
140 | } |
141 | |
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(ctx: s->ctx, error: isl_error_invalid, msg: "syntax error" , |
149 | __FILE__, __LINE__); |
150 | |
151 | if (s->ctx->opt->on_error == ISL_ON_ERROR_CONTINUE) |
152 | return; |
153 | fprintf(stderr, format: "syntax error (%d, %d): %s\n" , line, col, msg); |
154 | if (tok) { |
155 | if (tok->type < 256) |
156 | fprintf(stderr, format: "got '%c'\n" , tok->type); |
157 | else if (tok->type == ISL_TOKEN_IDENT) |
158 | fprintf(stderr, format: "got ident '%s'\n" , tok->u.s); |
159 | else if (tok->is_keyword) |
160 | fprintf(stderr, format: "got keyword '%s'\n" , tok->u.s); |
161 | else if (tok->type == ISL_TOKEN_VALUE) { |
162 | fprintf(stderr, format: "got value '" ); |
163 | isl_int_print(stderr, tok->u.v, 0); |
164 | fprintf(stderr, format: "'\n" ); |
165 | } else if (tok->type == ISL_TOKEN_MAP) { |
166 | isl_printer *p; |
167 | fprintf(stderr, format: "got map '" ); |
168 | p = isl_printer_to_file(ctx: s->ctx, stderr); |
169 | p = isl_printer_print_map(printer: p, map: tok->u.map); |
170 | isl_printer_free(printer: p); |
171 | fprintf(stderr, format: "'\n" ); |
172 | } else if (tok->type == ISL_TOKEN_AFF) { |
173 | isl_printer *p; |
174 | fprintf(stderr, format: "got affine expression '" ); |
175 | p = isl_printer_to_file(ctx: s->ctx, stderr); |
176 | p = isl_printer_print_pw_aff(p, pwaff: tok->u.pwaff); |
177 | isl_printer_free(printer: p); |
178 | fprintf(stderr, format: "'\n" ); |
179 | } else if (tok->u.s) |
180 | fprintf(stderr, format: "got token '%s'\n" , tok->u.s); |
181 | else |
182 | fprintf(stderr, format: "got token type %d\n" , tok->type); |
183 | } |
184 | if (s->ctx->opt->on_error == ISL_ON_ERROR_ABORT) |
185 | abort(); |
186 | } |
187 | |
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(ctx: 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 | |
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 | |
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 | */ |
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(stream: 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 | |
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 | */ |
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 | |
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 | |
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 | |
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(s1: s->buffer, s2: "exists" )) |
324 | return ISL_TOKEN_EXISTS; |
325 | if (!strcasecmp(s1: s->buffer, s2: "and" )) |
326 | return ISL_TOKEN_AND; |
327 | if (!strcasecmp(s1: s->buffer, s2: "or" )) |
328 | return ISL_TOKEN_OR; |
329 | if (!strcasecmp(s1: s->buffer, s2: "implies" )) |
330 | return ISL_TOKEN_IMPLIES; |
331 | if (!strcasecmp(s1: s->buffer, s2: "not" )) |
332 | return ISL_TOKEN_NOT; |
333 | if (!strcasecmp(s1: s->buffer, s2: "infty" )) |
334 | return ISL_TOKEN_INFTY; |
335 | if (!strcasecmp(s1: s->buffer, s2: "infinity" )) |
336 | return ISL_TOKEN_INFTY; |
337 | if (!strcasecmp(s1: s->buffer, s2: "NaN" )) |
338 | return ISL_TOKEN_NAN; |
339 | if (!strcasecmp(s1: s->buffer, s2: "min" )) |
340 | return ISL_TOKEN_MIN; |
341 | if (!strcasecmp(s1: s->buffer, s2: "max" )) |
342 | return ISL_TOKEN_MAX; |
343 | if (!strcasecmp(s1: s->buffer, s2: "rat" )) |
344 | return ISL_TOKEN_RAT; |
345 | if (!strcasecmp(s1: s->buffer, s2: "true" )) |
346 | return ISL_TOKEN_TRUE; |
347 | if (!strcasecmp(s1: s->buffer, s2: "false" )) |
348 | return ISL_TOKEN_FALSE; |
349 | if (!strcasecmp(s1: s->buffer, s2: "ceild" )) |
350 | return ISL_TOKEN_CEILD; |
351 | if (!strcasecmp(s1: s->buffer, s2: "floord" )) |
352 | return ISL_TOKEN_FLOORD; |
353 | if (!strcasecmp(s1: s->buffer, s2: "mod" )) |
354 | return ISL_TOKEN_MOD; |
355 | if (!strcasecmp(s1: s->buffer, s2: "ceil" )) |
356 | return ISL_TOKEN_CEIL; |
357 | if (!strcasecmp(s1: s->buffer, s2: "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: s->buffer); |
364 | entry = isl_hash_table_find(ctx: s->ctx, table: s->keywords, key_hash: name_hash, eq: same_name, |
365 | val: s->buffer, reserve: 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 | |
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 | |
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(ctx: s->ctx, line, col, on_new_line: 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(ctx: s->ctx, line, col, on_new_line: old_line != line); |
450 | if (!tok) |
451 | return NULL; |
452 | tok->u.s = strdup(s: "->" ); |
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(ctx: s->ctx, line, col, on_new_line: 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(ctx: s->ctx, line, col, on_new_line: 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, c: '\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(ctx: s->ctx, line, col, on_new_line: 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(ctx: s->ctx, line, col, on_new_line: 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, c: '\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: s->buffer); |
510 | if (!tok->u.s) |
511 | goto error; |
512 | return tok; |
513 | } |
514 | if (c == '"') { |
515 | tok = isl_token_new(ctx: s->ctx, line, col, on_new_line: 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, msg: "unterminated string" ); |
524 | goto error; |
525 | } |
526 | isl_stream_push_char(s, c: '\0'); |
527 | tok->u.s = strdup(s: s->buffer); |
528 | return tok; |
529 | } |
530 | if (c == '=') { |
531 | int c; |
532 | tok = isl_token_new(ctx: s->ctx, line, col, on_new_line: old_line != line); |
533 | if (!tok) |
534 | return NULL; |
535 | if ((c = isl_stream_getc(s)) == '=') { |
536 | tok->u.s = strdup(s: "==" ); |
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(ctx: s->ctx, line, col, on_new_line: old_line != line); |
548 | if (!tok) |
549 | return NULL; |
550 | if ((c = isl_stream_getc(s)) == '=') { |
551 | tok->u.s = strdup(s: ":=" ); |
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(ctx: s->ctx, line, col, on_new_line: old_line != line); |
563 | if (!tok) |
564 | return NULL; |
565 | if ((c = isl_stream_getc(s)) == '=') { |
566 | tok->u.s = strdup(s: ">=" ); |
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(s: ">>=" ); |
572 | tok->type = ISL_TOKEN_LEX_GE; |
573 | return tok; |
574 | } |
575 | tok->u.s = strdup(s: ">>" ); |
576 | tok->type = ISL_TOKEN_LEX_GT; |
577 | } else { |
578 | tok->u.s = strdup(s: ">" ); |
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(ctx: s->ctx, line, col, on_new_line: old_line != line); |
588 | if (!tok) |
589 | return NULL; |
590 | if ((c = isl_stream_getc(s)) == '=') { |
591 | tok->u.s = strdup(s: "<=" ); |
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(s: "<<=" ); |
597 | tok->type = ISL_TOKEN_LEX_LE; |
598 | return tok; |
599 | } |
600 | tok->u.s = strdup(s: "<<" ); |
601 | tok->type = ISL_TOKEN_LEX_LT; |
602 | } else { |
603 | tok->u.s = strdup(s: "<" ); |
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(ctx: s->ctx, line, col, on_new_line: 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(s: "&" ); |
617 | isl_stream_ungetc(s, c); |
618 | } else |
619 | tok->u.s = strdup(s: "&&" ); |
620 | return tok; |
621 | } |
622 | if (c == '|') { |
623 | tok = isl_token_new(ctx: s->ctx, line, col, on_new_line: 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(s: "|" ); |
629 | isl_stream_ungetc(s, c); |
630 | } else |
631 | tok->u.s = strdup(s: "||" ); |
632 | return tok; |
633 | } |
634 | if (c == '/') { |
635 | tok = isl_token_new(ctx: s->ctx, line, col, on_new_line: old_line != line); |
636 | if (!tok) |
637 | return NULL; |
638 | if ((c = isl_stream_getc(s)) == '\\') { |
639 | tok->u.s = strdup(s: "/\\" ); |
640 | tok->type = ISL_TOKEN_AND; |
641 | return tok; |
642 | } else if (c == '/') { |
643 | tok->u.s = strdup(s: "//" ); |
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(ctx: s->ctx, line, col, on_new_line: 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(s: "\\/" ); |
662 | tok->type = ISL_TOKEN_OR; |
663 | } |
664 | return tok; |
665 | } |
666 | if (c == '!') { |
667 | tok = isl_token_new(ctx: s->ctx, line, col, on_new_line: old_line != line); |
668 | if (!tok) |
669 | return NULL; |
670 | if ((c = isl_stream_getc(s)) == '=') { |
671 | tok->u.s = strdup(s: "!=" ); |
672 | tok->type = ISL_TOKEN_NE; |
673 | return tok; |
674 | } else { |
675 | tok->type = ISL_TOKEN_NOT; |
676 | tok->u.s = strdup(s: "!" ); |
677 | } |
678 | if (c != -1) |
679 | isl_stream_ungetc(s, c); |
680 | return tok; |
681 | } |
682 | |
683 | tok = isl_token_new(ctx: s->ctx, line, col, on_new_line: 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 | |
693 | struct isl_token *isl_stream_next_token(__isl_keep isl_stream *s) |
694 | { |
695 | return next_token(s, same_line: 0); |
696 | } |
697 | |
698 | struct isl_token *isl_stream_next_token_on_same_line(__isl_keep isl_stream *s) |
699 | { |
700 | return next_token(s, same_line: 1); |
701 | } |
702 | |
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 | |
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 | |
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(s: tok->u.s); |
740 | isl_token_free(tok); |
741 | return ident; |
742 | } |
743 | isl_stream_push_token(s, tok); |
744 | return NULL; |
745 | } |
746 | |
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, msg: "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, msg: "expecting other token" ); |
762 | isl_token_free(tok); |
763 | return -1; |
764 | } |
765 | |
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 | |
779 | static isl_stat free_keyword(void **p, void *user) |
780 | { |
781 | struct isl_keyword *keyword = *p; |
782 | |
783 | free(ptr: keyword->name); |
784 | free(ptr: keyword); |
785 | |
786 | return isl_stat_ok; |
787 | } |
788 | |
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(tok: s->tokens[i]); |
797 | s->n_token = 0; |
798 | } |
799 | |
800 | isl_ctx *isl_stream_get_ctx(__isl_keep isl_stream *s) |
801 | { |
802 | return s ? s->ctx : NULL; |
803 | } |
804 | |
805 | void isl_stream_free(__isl_take isl_stream *s) |
806 | { |
807 | if (!s) |
808 | return; |
809 | free(ptr: s->buffer); |
810 | if (s->n_token != 0) { |
811 | struct isl_token *tok = isl_stream_next_token(s); |
812 | isl_stream_error(s, tok, msg: "unexpected token" ); |
813 | isl_token_free(tok); |
814 | } |
815 | if (s->keywords) { |
816 | isl_hash_table_foreach(ctx: s->ctx, table: s->keywords, fn: &free_keyword, NULL); |
817 | isl_hash_table_free(ctx: s->ctx, table: s->keywords); |
818 | } |
819 | free(ptr: s->yaml_state); |
820 | free(ptr: s->yaml_indent); |
821 | isl_ctx_deref(ctx: s->ctx); |
822 | free(ptr: 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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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, type: '}')) |
980 | return isl_bool_false; |
981 | if (update_state(s, state: 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, msg: "unexpected EOF" ); |
989 | return isl_bool_error; |
990 | } |
991 | if (tok->type == ':') { |
992 | isl_token_free(tok); |
993 | if (update_state(s, state: isl_yaml_mapping_val) < 0) |
994 | return isl_bool_error; |
995 | return isl_bool_true; |
996 | } |
997 | isl_stream_error(s, tok, msg: "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, type: ',')) |
1003 | return isl_bool_false; |
1004 | if (update_state(s, state: 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, state: 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, type: ']')) |
1021 | return isl_bool_false; |
1022 | if (update_state(s, state: 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, msg: "unexpected EOF" ); |
1030 | return isl_bool_error; |
1031 | } |
1032 | if (tok->type == '-') { |
1033 | isl_token_free(tok); |
1034 | if (update_state(s, state: isl_yaml_sequence) < 0) |
1035 | return isl_bool_error; |
1036 | return isl_bool_true; |
1037 | } |
1038 | isl_stream_error(s, tok, msg: "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(b: isl_stream_eat_if_available(s, type: ',')); |
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 | */ |
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, state: 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, msg: "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 | */ |
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, type: '}') < 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 | */ |
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, state: 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, msg: "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 | */ |
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, type: ']') < 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 | |