source: mainline/uspace/app/bithenge/script.c@ c12b2ae

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c12b2ae was c12b2ae, checked in by Sean Bartell <wingedtachikoma@…>, 13 years ago

Bithenge: search for current node members in outer scopes

  • Property mode set to 100644
File size: 24.5 KB
Line 
1/*
2 * Copyright (c) 2012 Sean Bartell
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup bithenge
30 * @{
31 */
32/**
33 * @file
34 * Script parsing.
35 */
36
37#include <ctype.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include "expression.h"
41#include "os.h"
42#include "script.h"
43#include "sequence.h"
44#include "transform.h"
45#include "tree.h"
46
47/** Tokens with more characters than this may be read incorrectly. */
48#define MAX_TOKEN_SIZE 256
49#define BUFFER_SIZE 4096
50
51/** Single-character symbols are represented by the character itself. Every
52 * other token uses one of these values: */
53typedef enum {
54 TOKEN_ERROR = -128,
55 TOKEN_EOF,
56 TOKEN_IDENTIFIER,
57 TOKEN_INTEGER,
58 TOKEN_LEFT_ARROW,
59
60 /* Keywords */
61 TOKEN_DO,
62 TOKEN_ELSE,
63 TOKEN_FALSE,
64 TOKEN_IF,
65 TOKEN_REPEAT,
66 TOKEN_STRUCT,
67 TOKEN_SWITCH,
68 TOKEN_TRANSFORM,
69 TOKEN_TRUE,
70 TOKEN_WHILE,
71} token_type_t;
72
73/** Singly-linked list of named transforms. */
74typedef struct transform_list {
75 char *name;
76 bithenge_transform_t *transform;
77 struct transform_list *next;
78} transform_list_t;
79
80/** State kept by the parser. */
81typedef struct {
82 /** Rather than constantly checking return values, the parser uses this
83 * to indicate whether an error has occurred. */
84 int error;
85
86 /** The list of named transforms. */
87 transform_list_t *transform_list;
88
89 /** The name of the script file. */
90 const char *filename;
91 /** The script file being read from. */
92 FILE *file;
93 /** The buffer that holds script code. There is always a '\0' after the
94 * current position to prevent reading too far. */
95 char buffer[BUFFER_SIZE];
96 /** The start position within the buffer of the next unread token. */
97 size_t buffer_pos;
98 /** The start position within the buffer of the current token. */
99 size_t old_buffer_pos;
100 /** The line number of the current token. */
101 int lineno;
102 /** Added to a buffer position to find the column number. */
103 int line_offset;
104
105 /** The type of the current token. */
106 token_type_t token;
107 union {
108 /** The value of a TOKEN_IDENTIFIER token. Unless changed to
109 * NULL, it will be freed when the next token is read. */
110 char *token_string;
111 /** The value of a TOKEN_INTEGER token. */
112 bithenge_int_t token_int;
113 };
114
115 /** The names of the current transform's parameters. */
116 char **parameter_names;
117 /** The number of parameters. */
118 int num_params;
119} state_t;
120
121/** Free the previous token's data. This must be called before changing
122 * state->token. */
123static void done_with_token(state_t *state)
124{
125 if (state->token == TOKEN_IDENTIFIER)
126 free(state->token_string);
127 state->token = TOKEN_ERROR;
128}
129
130/** Note that an error has occurred if error is not EOK. */
131static void error_errno(state_t *state, int error)
132{
133 // Don't overwrite a previous error.
134 if (state->error == EOK && error != EOK) {
135 done_with_token(state);
136 state->token = TOKEN_ERROR;
137 state->error = error;
138 }
139}
140
141/** Note that a syntax error has occurred and print an error message. */
142static void syntax_error(state_t *state, const char *message)
143{
144 // Printing multiple errors is confusing.
145 if (state->error == EOK) {
146 size_t start_char = state->old_buffer_pos + state->line_offset;
147 size_t end_char = state->buffer_pos + state->line_offset;
148 size_t size = end_char - start_char;
149 fprintf(stderr, "%s:%d:", state->filename, state->lineno);
150 if (size <= 1)
151 fprintf(stderr, "%zd: ", start_char);
152 else
153 fprintf(stderr, "%zd-%zd: ", start_char, end_char - 1);
154 fprintf(stderr, "%s: \"%.*s\"\n", message, (int)size,
155 state->buffer + state->old_buffer_pos);
156 error_errno(state, EINVAL);
157 }
158}
159
160/** Ensure the buffer contains enough characters to read a token. */
161static void fill_buffer(state_t *state)
162{
163 if (state->buffer_pos + MAX_TOKEN_SIZE < BUFFER_SIZE)
164 return;
165
166 size_t empty_size = state->buffer_pos;
167 size_t used_size = BUFFER_SIZE - 1 - state->buffer_pos;
168 memmove(state->buffer, state->buffer + state->buffer_pos, used_size);
169 state->line_offset += state->buffer_pos;
170 state->buffer_pos = 0;
171
172 size_t read_size = fread(state->buffer + used_size, 1, empty_size,
173 state->file);
174 if (ferror(state->file))
175 error_errno(state, EIO);
176 state->buffer[used_size + read_size] = '\0';
177}
178
179/** Read the next token. */
180static void next_token(state_t *state)
181{
182 fill_buffer(state);
183 done_with_token(state);
184 state->old_buffer_pos = state->buffer_pos;
185 char ch = state->buffer[state->buffer_pos];
186 if (ch == '\0') {
187 state->token = TOKEN_EOF;
188 } else if (ch == '#') {
189 while (state->buffer[state->buffer_pos] != '\n'
190 && state->buffer[state->buffer_pos] != '\0') {
191 state->buffer_pos++;
192 fill_buffer(state);
193 }
194 next_token(state);
195 return;
196 } else if (isspace(ch)) {
197 // Will eventually reach the '\0' at the end
198 while (isspace(state->buffer[state->buffer_pos])) {
199 if (state->buffer[state->buffer_pos] == '\n') {
200 state->lineno++;
201 state->line_offset = -state->buffer_pos;
202 }
203 state->buffer_pos++;
204 }
205 next_token(state);
206 return;
207 } else if (isalpha(ch)) {
208 while (isalnum(state->buffer[state->buffer_pos])
209 || state->buffer[state->buffer_pos] == '_')
210 state->buffer_pos++;
211 char *value = str_ndup(state->buffer + state->old_buffer_pos,
212 state->buffer_pos - state->old_buffer_pos);
213 if (!value) {
214 error_errno(state, ENOMEM);
215 } else if (!str_cmp(value, "do")) {
216 state->token = TOKEN_DO;
217 free(value);
218 } else if (!str_cmp(value, "else")) {
219 state->token = TOKEN_ELSE;
220 free(value);
221 } else if (!str_cmp(value, "false")) {
222 state->token = TOKEN_FALSE;
223 free(value);
224 } else if (!str_cmp(value, "if")) {
225 state->token = TOKEN_IF;
226 free(value);
227 } else if (!str_cmp(value, "repeat")) {
228 state->token = TOKEN_REPEAT;
229 free(value);
230 } else if (!str_cmp(value, "struct")) {
231 state->token = TOKEN_STRUCT;
232 free(value);
233 } else if (!str_cmp(value, "switch")) {
234 state->token = TOKEN_SWITCH;
235 free(value);
236 } else if (!str_cmp(value, "transform")) {
237 state->token = TOKEN_TRANSFORM;
238 free(value);
239 } else if (!str_cmp(value, "true")) {
240 state->token = TOKEN_TRUE;
241 free(value);
242 } else if (!str_cmp(value, "while")) {
243 state->token = TOKEN_WHILE;
244 free(value);
245 } else {
246 state->token = TOKEN_IDENTIFIER;
247 state->token_string = value;
248 }
249 } else if (isdigit(ch)) {
250 while (isdigit(state->buffer[state->buffer_pos]))
251 state->buffer_pos++;
252 state->token = TOKEN_INTEGER;
253 int rc = bithenge_parse_int(state->buffer +
254 state->old_buffer_pos, &state->token_int);
255 error_errno(state, rc);
256 } else if (ch == '<') {
257 state->token = ch;
258 state->buffer_pos++;
259 if (state->buffer[state->buffer_pos] == '-') {
260 state->buffer_pos++;
261 state->token = TOKEN_LEFT_ARROW;
262 }
263 } else {
264 state->token = ch;
265 state->buffer_pos++;
266 }
267}
268
269/** Allocate memory and handle failure by setting the error in the state. The
270 * caller must check the state for errors before using the return value of this
271 * function. */
272static void *state_malloc(state_t *state, size_t size)
273{
274 if (state->error != EOK)
275 return NULL;
276 void *result = malloc(size);
277 if (result == NULL)
278 error_errno(state, ENOMEM);
279 return result;
280}
281
282/** Reallocate memory and handle failure by setting the error in the state. If
283 * an error occurs, the existing pointer will be returned. */
284static void *state_realloc(state_t *state, void *ptr, size_t size)
285{
286 if (state->error != EOK)
287 return ptr;
288 void *result = realloc(ptr, size);
289 if (result == NULL) {
290 error_errno(state, ENOMEM);
291 return ptr;
292 }
293 return result;
294}
295
296/** Expect and consume a certain token. If the next token is of the wrong type,
297 * an error is caused. */
298static void expect(state_t *state, token_type_t type)
299{
300 if (state->token != type) {
301 syntax_error(state, "unexpected");
302 return;
303 }
304 next_token(state);
305}
306
307/** Expect and consume an identifier token. If the next token is not an
308 * identifier, an error is caused and this function returns null. */
309static char *expect_identifier(state_t *state)
310{
311 if (state->token != TOKEN_IDENTIFIER) {
312 syntax_error(state, "unexpected (identifier expected)");
313 return NULL;
314 }
315 char *val = state->token_string;
316 state->token_string = NULL;
317 next_token(state);
318 return val;
319}
320
321/** Find a transform by name. A reference will be added to the transform.
322 * @return The found transform, or NULL if none was found. */
323static bithenge_transform_t *get_named_transform(state_t *state,
324 const char *name)
325{
326 for (transform_list_t *e = state->transform_list; e; e = e->next) {
327 if (!str_cmp(e->name, name)) {
328 bithenge_transform_inc_ref(e->transform);
329 return e->transform;
330 }
331 }
332 for (int i = 0; bithenge_primitive_transforms[i].name; i++) {
333 if (!str_cmp(bithenge_primitive_transforms[i].name, name)) {
334 bithenge_transform_t *xform =
335 bithenge_primitive_transforms[i].transform;
336 bithenge_transform_inc_ref(xform);
337 return xform;
338 }
339 }
340 return NULL;
341}
342
343/** Add a named transform. This function takes ownership of the name and a
344 * reference to the transform. If an error has occurred, either may be null. */
345static void add_named_transform(state_t *state, bithenge_transform_t *xform, char *name)
346{
347 transform_list_t *entry = state_malloc(state, sizeof(*entry));
348 if (state->error != EOK) {
349 free(name);
350 bithenge_transform_dec_ref(xform);
351 free(entry);
352 return;
353 }
354 entry->name = name;
355 entry->transform = xform;
356 entry->next = state->transform_list;
357 state->transform_list = entry;
358}
359
360static bithenge_transform_t *parse_transform(state_t *state);
361static bithenge_transform_t *parse_struct(state_t *state);
362
363static bithenge_expression_t *parse_expression(state_t *state)
364{
365 int rc;
366 if (state->token == TOKEN_TRUE || state->token == TOKEN_FALSE) {
367 bool val = state->token == TOKEN_TRUE;
368 next_token(state);
369 bithenge_node_t *node;
370 rc = bithenge_new_boolean_node(&node, val);
371 if (rc != EOK) {
372 error_errno(state, rc);
373 return NULL;
374 }
375
376 bithenge_expression_t *expr;
377 rc = bithenge_const_expression(&expr, node);
378 if (rc != EOK) {
379 error_errno(state, rc);
380 return NULL;
381 }
382
383 return expr;
384 } else if (state->token == TOKEN_INTEGER) {
385 bithenge_int_t val = state->token_int;
386 next_token(state);
387 bithenge_node_t *node;
388 rc = bithenge_new_integer_node(&node, val);
389 if (rc != EOK) {
390 error_errno(state, rc);
391 return NULL;
392 }
393
394 bithenge_expression_t *expr;
395 rc = bithenge_const_expression(&expr, node);
396 if (rc != EOK) {
397 error_errno(state, rc);
398 return NULL;
399 }
400
401 return expr;
402 } else if (state->token == TOKEN_IDENTIFIER) {
403 int i;
404 for (i = 0; i < state->num_params; i++)
405 if (!str_cmp(state->parameter_names[i],
406 state->token_string))
407 break;
408
409 if (i == state->num_params) {
410 syntax_error(state, "unknown identifier");
411 return NULL;
412 }
413
414 bithenge_expression_t *expr;
415 rc = bithenge_param_expression(&expr, i);
416 if (rc != EOK) {
417 error_errno(state, rc);
418 return NULL;
419 }
420
421 next_token(state);
422
423 return expr;
424 } else if (state->token == '.') {
425 next_token(state);
426
427 const char *id = expect_identifier(state);
428 bithenge_node_t *key = NULL;
429 bithenge_expression_t *expr = NULL;
430
431 if (state->error == EOK) {
432 rc = bithenge_new_string_node(&key, id, true);
433 id = NULL;
434 error_errno(state, rc);
435 }
436
437 if (state->error == EOK) {
438 rc = bithenge_scope_member_expression(&expr, key);
439 key = NULL;
440 if (rc != EOK)
441 expr = NULL;
442 error_errno(state, rc);
443 }
444
445 if (state->error != EOK) {
446 free((char *)id);
447 bithenge_node_dec_ref(key);
448 bithenge_expression_dec_ref(expr);
449 return NULL;
450 }
451
452 return expr;
453 } else {
454 syntax_error(state, "expression expected");
455 return NULL;
456 }
457}
458
459// state->token must be TOKEN_IDENTIFIER when this is called
460static bithenge_transform_t *parse_invocation(state_t *state)
461{
462 bithenge_transform_t *result = get_named_transform(state,
463 state->token_string);
464 if (!result)
465 syntax_error(state, "transform not found");
466 next_token(state);
467
468 bithenge_expression_t **params = NULL;
469 int num_params = 0;
470 if (state->token == '(') {
471 next_token(state);
472 while (state->error == EOK && state->token != ')') {
473 if (num_params)
474 expect(state, ',');
475 params = state_realloc(state, params,
476 (num_params + 1)*sizeof(*params));
477 if (state->error != EOK)
478 break;
479 params[num_params] = parse_expression(state);
480 num_params++;
481 }
482 expect(state, ')');
483 }
484
485 /* TODO: show correct error position */
486 if (state->error == EOK
487 && bithenge_transform_num_params(result) != num_params)
488 syntax_error(state, "incorrect number of parameters before");
489
490 if (state->error != EOK) {
491 while (num_params--)
492 bithenge_expression_dec_ref(params[num_params]);
493 free(params);
494 bithenge_transform_dec_ref(result);
495 return NULL;
496 }
497
498 if (num_params) {
499 int rc = bithenge_param_wrapper(&result, result, params);
500 if (rc != EOK) {
501 error_errno(state, rc);
502 result = NULL;
503 }
504 }
505
506 return result;
507}
508
509/** Create a transform that just produces an empty node.
510 * @param state The parser state.
511 * @return The new transform, or NULL on error. */
512static bithenge_transform_t *make_empty_transform(state_t *state)
513{
514 bithenge_node_t *node;
515 int rc = bithenge_new_empty_internal_node(&node);
516 if (rc != EOK) {
517 error_errno(state, rc);
518 return NULL;
519 }
520
521 bithenge_expression_t *expr;
522 rc = bithenge_const_expression(&expr, node);
523 if (rc != EOK) {
524 error_errno(state, rc);
525 return NULL;
526 }
527
528 bithenge_transform_t *xform;
529 rc = bithenge_expression_transform(&xform, expr);
530 if (rc != EOK) {
531 error_errno(state, rc);
532 return NULL;
533 }
534
535 return xform;
536}
537
538static bithenge_transform_t *parse_if(state_t *state, bool in_struct)
539{
540 expect(state, TOKEN_IF);
541 expect(state, '(');
542 bithenge_expression_t *expr = parse_expression(state);
543 expect(state, ')');
544 expect(state, '{');
545 bithenge_transform_t *true_xform =
546 in_struct ? parse_struct(state) : parse_transform(state);
547 expect(state, '}');
548
549 bithenge_transform_t *false_xform = NULL;
550 if (state->token == TOKEN_ELSE) {
551 next_token(state);
552 expect(state, '{');
553 false_xform =
554 in_struct ? parse_struct(state) : parse_transform(state);
555 expect(state, '}');
556 } else {
557 if (in_struct)
558 false_xform = make_empty_transform(state);
559 else
560 syntax_error(state, "else expected");
561 }
562
563 if (state->error != EOK) {
564 bithenge_expression_dec_ref(expr);
565 bithenge_transform_dec_ref(true_xform);
566 bithenge_transform_dec_ref(false_xform);
567 return NULL;
568 }
569
570 bithenge_transform_t *if_xform;
571 int rc = bithenge_if_transform(&if_xform, expr, true_xform,
572 false_xform);
573 if (rc != EOK) {
574 error_errno(state, rc);
575 return NULL;
576 }
577 return if_xform;
578}
579
580static bithenge_transform_t *parse_switch(state_t *state, bool in_struct)
581{
582 expect(state, TOKEN_SWITCH);
583 expect(state, '(');
584 bithenge_expression_t *ref_expr = parse_expression(state);
585 expect(state, ')');
586 expect(state, '{');
587 int num = 0;
588 bithenge_expression_t **exprs = NULL;
589 bithenge_transform_t **xforms = NULL;
590 while (state->error == EOK && state->token != '}') {
591 bithenge_expression_t *expr;
592 if (state->token == TOKEN_ELSE) {
593 next_token(state);
594 bithenge_node_t *node;
595 int rc = bithenge_new_boolean_node(&node, true);
596 if (rc != EOK) {
597 error_errno(state, rc);
598 break;
599 }
600 rc = bithenge_const_expression(&expr, node);
601 if (rc != EOK) {
602 error_errno(state, rc);
603 break;
604 }
605 } else {
606 expr = parse_expression(state);
607 if (state->error == EOK) {
608 bithenge_expression_inc_ref(ref_expr);
609 int rc = bithenge_binary_expression(&expr,
610 BITHENGE_EXPRESSION_EQUALS, ref_expr,
611 expr);
612 if (rc != EOK) {
613 error_errno(state, rc);
614 break;
615 }
616 }
617 }
618
619 expect(state, ':');
620 bithenge_transform_t *xform;
621 if (in_struct) {
622 expect(state, '{');
623 xform = parse_struct(state);
624 expect(state, '}');
625 } else
626 xform = parse_transform(state);
627 expect(state, ';');
628
629 exprs = state_realloc(state, exprs,
630 sizeof(*exprs) * (num + 1));
631 xforms = state_realloc(state, xforms,
632 sizeof(*xforms) * (num + 1));
633 if (state->error != EOK) {
634 bithenge_expression_dec_ref(expr);
635 bithenge_transform_dec_ref(xform);
636 break;
637 }
638
639 exprs[num] = expr;
640 xforms[num] = xform;
641 num++;
642 }
643 bithenge_expression_dec_ref(ref_expr);
644
645 bithenge_transform_t *switch_xform = &bithenge_invalid_transform;
646 bithenge_transform_inc_ref(switch_xform);
647 while (state->error == EOK && num >= 1) {
648 num--;
649 int rc = bithenge_if_transform(&switch_xform, exprs[num],
650 xforms[num], switch_xform);
651 if (rc != EOK)
652 error_errno(state, rc);
653 }
654
655 while (num >= 1) {
656 num--;
657 bithenge_expression_dec_ref(exprs[num]);
658 bithenge_transform_dec_ref(xforms[num]);
659 }
660 free(exprs);
661 free(xforms);
662
663 expect(state, '}');
664 return switch_xform;
665}
666
667static bithenge_transform_t *parse_repeat(state_t *state)
668{
669 expect(state, TOKEN_REPEAT);
670 bithenge_expression_t *expr = NULL;
671 if (state->token == '(') {
672 next_token(state);
673 expr = parse_expression(state);
674 expect(state, ')');
675 }
676 expect(state, '{');
677 bithenge_transform_t *xform = parse_transform(state);
678 expect(state, '}');
679
680 if (state->error != EOK) {
681 bithenge_expression_dec_ref(expr);
682 bithenge_transform_dec_ref(xform);
683 return NULL;
684 }
685
686 bithenge_transform_t *repeat_xform;
687 int rc = bithenge_repeat_transform(&repeat_xform, xform, expr);
688 if (rc != EOK) {
689 error_errno(state, rc);
690 return NULL;
691 }
692 return repeat_xform;
693}
694
695static bithenge_transform_t *parse_do_while(state_t *state)
696{
697 expect(state, TOKEN_DO);
698 expect(state, '{');
699 bithenge_transform_t *xform = parse_transform(state);
700 expect(state, '}');
701 expect(state, TOKEN_WHILE);
702 expect(state, '(');
703 bithenge_expression_t *expr = parse_expression(state);
704 expect(state, ')');
705
706 if (state->error != EOK) {
707 bithenge_expression_dec_ref(expr);
708 bithenge_transform_dec_ref(xform);
709 return NULL;
710 }
711
712 bithenge_transform_t *do_while_xform;
713 int rc = bithenge_do_while_transform(&do_while_xform, xform, expr);
714 if (rc != EOK) {
715 error_errno(state, rc);
716 return NULL;
717 }
718 return do_while_xform;
719}
720
721/* The TOKEN_STRUCT and '{' must already have been skipped. */
722static bithenge_transform_t *parse_struct(state_t *state)
723{
724 size_t num = 0;
725 bithenge_named_transform_t *subxforms;
726 /* We keep an extra space for the {NULL, NULL} terminator. */
727 subxforms = state_malloc(state, sizeof(*subxforms));
728 while (state->error == EOK && state->token != '}') {
729 if (state->token == TOKEN_IF) {
730 subxforms[num].transform = parse_if(state, true);
731 subxforms[num].name = NULL;
732 } else if (state->token == TOKEN_SWITCH) {
733 subxforms[num].transform = parse_switch(state, true);
734 subxforms[num].name = NULL;
735 } else {
736 if (state->token == '.') {
737 next_token(state);
738 subxforms[num].name = expect_identifier(state);
739 } else {
740 subxforms[num].name = NULL;
741 }
742 expect(state, TOKEN_LEFT_ARROW);
743 subxforms[num].transform = parse_transform(state);
744 expect(state, ';');
745 }
746 num++;
747 subxforms = state_realloc(state, subxforms,
748 (num + 1)*sizeof(*subxforms));
749 }
750
751 if (state->error != EOK) {
752 while (num--) {
753 free((void *)subxforms[num].name);
754 bithenge_transform_dec_ref(subxforms[num].transform);
755 }
756 free(subxforms);
757 return NULL;
758 }
759
760 subxforms[num].name = NULL;
761 subxforms[num].transform = NULL;
762 bithenge_transform_t *result;
763 int rc = bithenge_new_struct(&result, subxforms);
764 if (rc != EOK) {
765 error_errno(state, rc);
766 return NULL;
767 }
768 return result;
769}
770
771/** Parse a transform without composition.
772 * @return The parsed transform, or NULL if an error occurred. */
773static bithenge_transform_t *parse_transform_no_compose(state_t *state)
774{
775 if (state->token == TOKEN_DO) {
776 return parse_do_while(state);
777 } else if (state->token == TOKEN_IDENTIFIER) {
778 return parse_invocation(state);
779 } else if (state->token == TOKEN_IF) {
780 return parse_if(state, false);
781 } else if (state->token == TOKEN_REPEAT) {
782 return parse_repeat(state);
783 } else if (state->token == TOKEN_STRUCT) {
784 next_token(state);
785 expect(state, '{');
786 bithenge_transform_t *xform = parse_struct(state);
787 expect(state, '}');
788 return xform;
789 } else if (state->token == TOKEN_SWITCH) {
790 return parse_switch(state, false);
791 } else {
792 syntax_error(state, "unexpected (transform expected)");
793 return NULL;
794 }
795}
796
797/** Parse a transform.
798 * @return The parsed transform, or NULL if an error occurred. */
799static bithenge_transform_t *parse_transform(state_t *state)
800{
801 bithenge_transform_t *result = parse_transform_no_compose(state);
802 bithenge_transform_t **xforms = NULL;
803 size_t num = 1;
804 while (state->token == TOKEN_LEFT_ARROW) {
805 expect(state, TOKEN_LEFT_ARROW);
806 xforms = state_realloc(state, xforms,
807 (num + 1) * sizeof(*xforms));
808 if (state->error != EOK)
809 break;
810 xforms[num] = parse_transform_no_compose(state);
811 num++;
812 }
813 if (state->error != EOK) {
814 while (xforms && num--)
815 bithenge_transform_dec_ref(xforms[num]);
816 free(xforms);
817 bithenge_transform_dec_ref(result);
818 return NULL;
819 }
820 if (xforms) {
821 xforms[0] = result;
822 int rc = bithenge_new_composed_transform(&result, xforms, num);
823 if (rc != EOK) {
824 error_errno(state, rc);
825 return NULL;
826 }
827 }
828 return result;
829}
830
831/** Parse a definition. */
832static void parse_definition(state_t *state)
833{
834 expect(state, TOKEN_TRANSFORM);
835 char *name = expect_identifier(state);
836
837 if (state->token == '(') {
838 next_token(state);
839 while (state->error == EOK && state->token != ')') {
840 if (state->num_params)
841 expect(state, ',');
842 state->parameter_names = state_realloc(state,
843 state->parameter_names,
844 (state->num_params + 1)*sizeof(*state->parameter_names));
845 if (state->error != EOK)
846 break;
847 state->parameter_names[state->num_params++] =
848 expect_identifier(state);
849 }
850 expect(state, ')');
851 }
852
853 expect(state, '=');
854 bithenge_transform_t *xform = parse_transform(state);
855 expect(state, ';');
856
857 if (state->error == EOK) {
858 int rc = bithenge_new_barrier_transform(&xform, xform,
859 state->num_params);
860 if (rc != EOK) {
861 xform = NULL;
862 error_errno(state, rc);
863 }
864 }
865
866 add_named_transform(state, xform, name);
867
868 for (int i = 0; i < state->num_params; i++)
869 free(state->parameter_names[i]);
870 free(state->parameter_names);
871 state->parameter_names = NULL;
872 state->num_params = 0;
873}
874
875/** Initialize the state. */
876static void state_init(state_t *state, const char *filename)
877{
878 state->error = EOK;
879 state->transform_list = NULL;
880 state->parameter_names = NULL;
881 state->num_params = 0;
882 state->token = TOKEN_ERROR;
883 state->old_buffer_pos = state->buffer_pos = BUFFER_SIZE - 1;
884 state->lineno = 1;
885 state->line_offset = (int)-state->buffer_pos + 1;
886 state->filename = filename;
887 state->file = fopen(filename, "r");
888 if (!state->file) {
889 error_errno(state, errno);
890 } else {
891 next_token(state);
892 }
893}
894
895/** Destroy the state. */
896static void state_destroy(state_t *state)
897{
898 done_with_token(state);
899 state->token = TOKEN_ERROR;
900 if (state->file)
901 fclose(state->file);
902 transform_list_t *entry = state->transform_list;
903 while (entry) {
904 transform_list_t *next = entry->next;
905 free(entry->name);
906 bithenge_transform_dec_ref(entry->transform);
907 free(entry);
908 entry = next;
909 }
910 for (int i = 0; i < state->num_params; i++)
911 free(state->parameter_names[i]);
912 free(state->parameter_names);
913}
914
915/** Parse a script file.
916 * @param filename The name of the script file.
917 * @param[out] out Stores the "main" transform.
918 * @return EOK on success, EINVAL on syntax error, or an error code from
919 * errno.h. */
920int bithenge_parse_script(const char *filename, bithenge_transform_t **out)
921{
922 state_t state;
923 state_init(&state, filename);
924 while (state.error == EOK && state.token != TOKEN_EOF)
925 parse_definition(&state);
926 *out = get_named_transform(&state, "main");
927 int rc = state.error;
928 state_destroy(&state);
929 if (rc == EOK && !*out) {
930 fprintf(stderr, "no \"main\" transform\n");
931 rc = EINVAL;
932 }
933 return rc;
934}
935
936/** @}
937 */
Note: See TracBrowser for help on using the repository browser.