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

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

Bithenge: add do{}while() transforms

  • 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 if (state->token == TOKEN_TRUE || state->token == TOKEN_FALSE) {
366 bool val = state->token == TOKEN_TRUE;
367 next_token(state);
368 bithenge_node_t *node;
369 int rc = bithenge_new_boolean_node(&node, val);
370 if (rc != EOK) {
371 error_errno(state, rc);
372 return NULL;
373 }
374
375 bithenge_expression_t *expr;
376 rc = bithenge_const_expression(&expr, node);
377 if (rc != EOK) {
378 error_errno(state, rc);
379 return NULL;
380 }
381
382 return expr;
383 } else if (state->token == TOKEN_INTEGER) {
384 bithenge_int_t val = state->token_int;
385 next_token(state);
386 bithenge_node_t *node;
387 int rc = bithenge_new_integer_node(&node, val);
388 if (rc != EOK) {
389 error_errno(state, rc);
390 return NULL;
391 }
392
393 bithenge_expression_t *expr;
394 rc = bithenge_const_expression(&expr, node);
395 if (rc != EOK) {
396 error_errno(state, rc);
397 return NULL;
398 }
399
400 return expr;
401 } else if (state->token == TOKEN_IDENTIFIER) {
402 int i;
403 for (i = 0; i < state->num_params; i++)
404 if (!str_cmp(state->parameter_names[i],
405 state->token_string))
406 break;
407
408 if (i == state->num_params) {
409 syntax_error(state, "unknown identifier");
410 return NULL;
411 }
412
413 bithenge_expression_t *expr;
414 int rc = bithenge_param_expression(&expr, i);
415 if (rc != EOK) {
416 error_errno(state, rc);
417 return NULL;
418 }
419
420 next_token(state);
421
422 return expr;
423 } else if (state->token == '.') {
424 next_token(state);
425
426 const char *id = expect_identifier(state);
427 bithenge_node_t *key = NULL;
428 bithenge_expression_t *expr = NULL;
429 int rc = bithenge_current_node_expression(&expr);
430 error_errno(state, rc);
431
432 if (state->error == EOK) {
433 rc = bithenge_new_string_node(&key, id, true);
434 id = NULL;
435 error_errno(state, rc);
436 }
437
438 if (state->error == EOK) {
439 rc = bithenge_member_expression(&expr, expr, key);
440 key = NULL;
441 if (rc != EOK)
442 expr = NULL;
443 error_errno(state, rc);
444 }
445
446 if (state->error != EOK) {
447 free((char *)id);
448 bithenge_node_dec_ref(key);
449 bithenge_expression_dec_ref(expr);
450 return NULL;
451 }
452
453 return expr;
454 } else {
455 syntax_error(state, "expression expected");
456 return NULL;
457 }
458}
459
460// state->token must be TOKEN_IDENTIFIER when this is called
461static bithenge_transform_t *parse_invocation(state_t *state)
462{
463 bithenge_transform_t *result = get_named_transform(state,
464 state->token_string);
465 if (!result)
466 syntax_error(state, "transform not found");
467 next_token(state);
468
469 bithenge_expression_t **params = NULL;
470 int num_params = 0;
471 if (state->token == '(') {
472 next_token(state);
473 while (state->error == EOK && state->token != ')') {
474 if (num_params)
475 expect(state, ',');
476 params = state_realloc(state, params,
477 (num_params + 1)*sizeof(*params));
478 if (state->error != EOK)
479 break;
480 params[num_params] = parse_expression(state);
481 num_params++;
482 }
483 expect(state, ')');
484 }
485
486 /* TODO: show correct error position */
487 if (state->error == EOK
488 && bithenge_transform_num_params(result) != num_params)
489 syntax_error(state, "incorrect number of parameters before");
490
491 if (state->error != EOK) {
492 while (num_params--)
493 bithenge_expression_dec_ref(params[num_params]);
494 free(params);
495 bithenge_transform_dec_ref(result);
496 return NULL;
497 }
498
499 if (num_params) {
500 int rc = bithenge_param_wrapper(&result, result, params);
501 if (rc != EOK) {
502 error_errno(state, rc);
503 result = NULL;
504 }
505 }
506
507 return result;
508}
509
510/** Create a transform that just produces an empty node.
511 * @param state The parser state.
512 * @return The new transform, or NULL on error. */
513static bithenge_transform_t *make_empty_transform(state_t *state)
514{
515 bithenge_node_t *node;
516 int rc = bithenge_new_empty_internal_node(&node);
517 if (rc != EOK) {
518 error_errno(state, rc);
519 return NULL;
520 }
521
522 bithenge_expression_t *expr;
523 rc = bithenge_const_expression(&expr, node);
524 if (rc != EOK) {
525 error_errno(state, rc);
526 return NULL;
527 }
528
529 bithenge_transform_t *xform;
530 rc = bithenge_expression_transform(&xform, expr);
531 if (rc != EOK) {
532 error_errno(state, rc);
533 return NULL;
534 }
535
536 return xform;
537}
538
539static bithenge_transform_t *parse_if(state_t *state, bool in_struct)
540{
541 expect(state, TOKEN_IF);
542 expect(state, '(');
543 bithenge_expression_t *expr = parse_expression(state);
544 expect(state, ')');
545 expect(state, '{');
546 bithenge_transform_t *true_xform =
547 in_struct ? parse_struct(state) : parse_transform(state);
548 expect(state, '}');
549
550 bithenge_transform_t *false_xform = NULL;
551 if (state->token == TOKEN_ELSE) {
552 next_token(state);
553 expect(state, '{');
554 false_xform =
555 in_struct ? parse_struct(state) : parse_transform(state);
556 expect(state, '}');
557 } else {
558 if (in_struct)
559 false_xform = make_empty_transform(state);
560 else
561 syntax_error(state, "else expected");
562 }
563
564 if (state->error != EOK) {
565 bithenge_expression_dec_ref(expr);
566 bithenge_transform_dec_ref(true_xform);
567 bithenge_transform_dec_ref(false_xform);
568 return NULL;
569 }
570
571 bithenge_transform_t *if_xform;
572 int rc = bithenge_if_transform(&if_xform, expr, true_xform,
573 false_xform);
574 if (rc != EOK) {
575 error_errno(state, rc);
576 return NULL;
577 }
578 return if_xform;
579}
580
581static bithenge_transform_t *parse_switch(state_t *state, bool in_struct)
582{
583 expect(state, TOKEN_SWITCH);
584 expect(state, '(');
585 bithenge_expression_t *ref_expr = parse_expression(state);
586 expect(state, ')');
587 expect(state, '{');
588 int num = 0;
589 bithenge_expression_t **exprs = NULL;
590 bithenge_transform_t **xforms = NULL;
591 while (state->error == EOK && state->token != '}') {
592 bithenge_expression_t *expr;
593 if (state->token == TOKEN_ELSE) {
594 next_token(state);
595 bithenge_node_t *node;
596 int rc = bithenge_new_boolean_node(&node, true);
597 if (rc != EOK) {
598 error_errno(state, rc);
599 break;
600 }
601 rc = bithenge_const_expression(&expr, node);
602 if (rc != EOK) {
603 error_errno(state, rc);
604 break;
605 }
606 } else {
607 expr = parse_expression(state);
608 if (state->error == EOK) {
609 bithenge_expression_inc_ref(ref_expr);
610 int rc = bithenge_binary_expression(&expr,
611 BITHENGE_EXPRESSION_EQUALS, ref_expr,
612 expr);
613 if (rc != EOK) {
614 error_errno(state, rc);
615 break;
616 }
617 }
618 }
619
620 expect(state, ':');
621 bithenge_transform_t *xform;
622 if (in_struct) {
623 expect(state, '{');
624 xform = parse_struct(state);
625 expect(state, '}');
626 } else
627 xform = parse_transform(state);
628 expect(state, ';');
629
630 exprs = state_realloc(state, exprs,
631 sizeof(*exprs) * (num + 1));
632 xforms = state_realloc(state, xforms,
633 sizeof(*xforms) * (num + 1));
634 if (state->error != EOK) {
635 bithenge_expression_dec_ref(expr);
636 bithenge_transform_dec_ref(xform);
637 break;
638 }
639
640 exprs[num] = expr;
641 xforms[num] = xform;
642 num++;
643 }
644 bithenge_expression_dec_ref(ref_expr);
645
646 bithenge_transform_t *switch_xform = &bithenge_invalid_transform;
647 bithenge_transform_inc_ref(switch_xform);
648 while (state->error == EOK && num >= 1) {
649 num--;
650 int rc = bithenge_if_transform(&switch_xform, exprs[num],
651 xforms[num], switch_xform);
652 if (rc != EOK)
653 error_errno(state, rc);
654 }
655
656 while (num >= 1) {
657 num--;
658 bithenge_expression_dec_ref(exprs[num]);
659 bithenge_transform_dec_ref(xforms[num]);
660 }
661 free(exprs);
662 free(xforms);
663
664 expect(state, '}');
665 return switch_xform;
666}
667
668static bithenge_transform_t *parse_repeat(state_t *state)
669{
670 expect(state, TOKEN_REPEAT);
671 bithenge_expression_t *expr = NULL;
672 if (state->token == '(') {
673 next_token(state);
674 expr = parse_expression(state);
675 expect(state, ')');
676 }
677 expect(state, '{');
678 bithenge_transform_t *xform = parse_transform(state);
679 expect(state, '}');
680
681 if (state->error != EOK) {
682 bithenge_expression_dec_ref(expr);
683 bithenge_transform_dec_ref(xform);
684 return NULL;
685 }
686
687 bithenge_transform_t *repeat_xform;
688 int rc = bithenge_repeat_transform(&repeat_xform, xform, expr);
689 if (rc != EOK) {
690 error_errno(state, rc);
691 return NULL;
692 }
693 return repeat_xform;
694}
695
696static bithenge_transform_t *parse_do_while(state_t *state)
697{
698 expect(state, TOKEN_DO);
699 expect(state, '{');
700 bithenge_transform_t *xform = parse_transform(state);
701 expect(state, '}');
702 expect(state, TOKEN_WHILE);
703 expect(state, '(');
704 bithenge_expression_t *expr = parse_expression(state);
705 expect(state, ')');
706
707 if (state->error != EOK) {
708 bithenge_expression_dec_ref(expr);
709 bithenge_transform_dec_ref(xform);
710 return NULL;
711 }
712
713 bithenge_transform_t *do_while_xform;
714 int rc = bithenge_do_while_transform(&do_while_xform, xform, expr);
715 if (rc != EOK) {
716 error_errno(state, rc);
717 return NULL;
718 }
719 return do_while_xform;
720}
721
722/* The TOKEN_STRUCT and '{' must already have been skipped. */
723static bithenge_transform_t *parse_struct(state_t *state)
724{
725 size_t num = 0;
726 bithenge_named_transform_t *subxforms;
727 /* We keep an extra space for the {NULL, NULL} terminator. */
728 subxforms = state_malloc(state, sizeof(*subxforms));
729 while (state->error == EOK && state->token != '}') {
730 if (state->token == TOKEN_IF) {
731 subxforms[num].transform = parse_if(state, true);
732 subxforms[num].name = NULL;
733 } else if (state->token == TOKEN_SWITCH) {
734 subxforms[num].transform = parse_switch(state, true);
735 subxforms[num].name = NULL;
736 } else {
737 if (state->token == '.') {
738 next_token(state);
739 subxforms[num].name = expect_identifier(state);
740 } else {
741 subxforms[num].name = NULL;
742 }
743 expect(state, TOKEN_LEFT_ARROW);
744 subxforms[num].transform = parse_transform(state);
745 expect(state, ';');
746 }
747 num++;
748 subxforms = state_realloc(state, subxforms,
749 (num + 1)*sizeof(*subxforms));
750 }
751
752 if (state->error != EOK) {
753 while (num--) {
754 free((void *)subxforms[num].name);
755 bithenge_transform_dec_ref(subxforms[num].transform);
756 }
757 free(subxforms);
758 return NULL;
759 }
760
761 subxforms[num].name = NULL;
762 subxforms[num].transform = NULL;
763 bithenge_transform_t *result;
764 int rc = bithenge_new_struct(&result, subxforms);
765 if (rc != EOK) {
766 error_errno(state, rc);
767 return NULL;
768 }
769 return result;
770}
771
772/** Parse a transform without composition.
773 * @return The parsed transform, or NULL if an error occurred. */
774static bithenge_transform_t *parse_transform_no_compose(state_t *state)
775{
776 if (state->token == TOKEN_DO) {
777 return parse_do_while(state);
778 } else if (state->token == TOKEN_IDENTIFIER) {
779 return parse_invocation(state);
780 } else if (state->token == TOKEN_IF) {
781 return parse_if(state, false);
782 } else if (state->token == TOKEN_REPEAT) {
783 return parse_repeat(state);
784 } else if (state->token == TOKEN_STRUCT) {
785 next_token(state);
786 expect(state, '{');
787 bithenge_transform_t *xform = parse_struct(state);
788 expect(state, '}');
789 return xform;
790 } else if (state->token == TOKEN_SWITCH) {
791 return parse_switch(state, false);
792 } else {
793 syntax_error(state, "unexpected (transform expected)");
794 return NULL;
795 }
796}
797
798/** Parse a transform.
799 * @return The parsed transform, or NULL if an error occurred. */
800static bithenge_transform_t *parse_transform(state_t *state)
801{
802 bithenge_transform_t *result = parse_transform_no_compose(state);
803 bithenge_transform_t **xforms = NULL;
804 size_t num = 1;
805 while (state->token == TOKEN_LEFT_ARROW) {
806 expect(state, TOKEN_LEFT_ARROW);
807 xforms = state_realloc(state, xforms,
808 (num + 1) * sizeof(*xforms));
809 if (state->error != EOK)
810 break;
811 xforms[num] = parse_transform_no_compose(state);
812 num++;
813 }
814 if (state->error != EOK) {
815 while (xforms && num--)
816 bithenge_transform_dec_ref(xforms[num]);
817 free(xforms);
818 bithenge_transform_dec_ref(result);
819 return NULL;
820 }
821 if (xforms) {
822 xforms[0] = result;
823 int rc = bithenge_new_composed_transform(&result, xforms, num);
824 if (rc != EOK) {
825 error_errno(state, rc);
826 return NULL;
827 }
828 }
829 return result;
830}
831
832/** Parse a definition. */
833static void parse_definition(state_t *state)
834{
835 expect(state, TOKEN_TRANSFORM);
836 char *name = expect_identifier(state);
837
838 if (state->token == '(') {
839 next_token(state);
840 while (state->error == EOK && state->token != ')') {
841 if (state->num_params)
842 expect(state, ',');
843 state->parameter_names = state_realloc(state,
844 state->parameter_names,
845 (state->num_params + 1)*sizeof(*state->parameter_names));
846 if (state->error != EOK)
847 break;
848 state->parameter_names[state->num_params++] =
849 expect_identifier(state);
850 }
851 expect(state, ')');
852 }
853
854 expect(state, '=');
855 bithenge_transform_t *xform = parse_transform(state);
856 expect(state, ';');
857
858 if (state->error == EOK) {
859 int rc = bithenge_new_scope_transform(&xform, xform,
860 state->num_params);
861 if (rc != EOK) {
862 xform = NULL;
863 error_errno(state, rc);
864 }
865 }
866
867 add_named_transform(state, xform, name);
868
869 for (int i = 0; i < state->num_params; i++)
870 free(state->parameter_names[i]);
871 free(state->parameter_names);
872 state->parameter_names = NULL;
873 state->num_params = 0;
874}
875
876/** Initialize the state. */
877static void state_init(state_t *state, const char *filename)
878{
879 state->error = EOK;
880 state->transform_list = NULL;
881 state->parameter_names = NULL;
882 state->num_params = 0;
883 state->token = TOKEN_ERROR;
884 state->old_buffer_pos = state->buffer_pos = BUFFER_SIZE - 1;
885 state->lineno = 1;
886 state->line_offset = (int)-state->buffer_pos + 1;
887 state->filename = filename;
888 state->file = fopen(filename, "r");
889 if (!state->file) {
890 error_errno(state, errno);
891 } else {
892 next_token(state);
893 }
894}
895
896/** Destroy the state. */
897static void state_destroy(state_t *state)
898{
899 done_with_token(state);
900 state->token = TOKEN_ERROR;
901 if (state->file)
902 fclose(state->file);
903 transform_list_t *entry = state->transform_list;
904 while (entry) {
905 transform_list_t *next = entry->next;
906 free(entry->name);
907 bithenge_transform_dec_ref(entry->transform);
908 free(entry);
909 entry = next;
910 }
911 for (int i = 0; i < state->num_params; i++)
912 free(state->parameter_names[i]);
913 free(state->parameter_names);
914}
915
916/** Parse a script file.
917 * @param filename The name of the script file.
918 * @param[out] out Stores the "main" transform.
919 * @return EOK on success, EINVAL on syntax error, or an error code from
920 * errno.h. */
921int bithenge_parse_script(const char *filename, bithenge_transform_t **out)
922{
923 state_t state;
924 state_init(&state, filename);
925 while (state.error == EOK && state.token != TOKEN_EOF)
926 parse_definition(&state);
927 *out = get_named_transform(&state, "main");
928 int rc = state.error;
929 state_destroy(&state);
930 if (rc == EOK && !*out) {
931 fprintf(stderr, "no \"main\" transform\n");
932 rc = EINVAL;
933 }
934 return rc;
935}
936
937/** @}
938 */
Note: See TracBrowser for help on using the repository browser.