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

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

Bithenge: add syntax for inputless transforms

  • Property mode set to 100644
File size: 27.6 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_EQUALS = -128,
55 TOKEN_ERROR,
56 TOKEN_EOF,
57 TOKEN_IDENTIFIER,
58 TOKEN_INTEGER,
59 TOKEN_LEFT_ARROW,
60
61 /* Keywords */
62 TOKEN_DO,
63 TOKEN_ELSE,
64 TOKEN_FALSE,
65 TOKEN_IF,
66 TOKEN_IN,
67 TOKEN_REPEAT,
68 TOKEN_STRUCT,
69 TOKEN_SWITCH,
70 TOKEN_TRANSFORM,
71 TOKEN_TRUE,
72 TOKEN_WHILE,
73} token_type_t;
74
75/** Singly-linked list of named transforms. */
76typedef struct transform_list {
77 char *name;
78 bithenge_transform_t *transform;
79 struct transform_list *next;
80} transform_list_t;
81
82/** State kept by the parser. */
83typedef struct {
84 /** Rather than constantly checking return values, the parser uses this
85 * to indicate whether an error has occurred. */
86 int error;
87
88 /** The list of named transforms. */
89 transform_list_t *transform_list;
90
91 /** The name of the script file. */
92 const char *filename;
93 /** The script file being read from. */
94 FILE *file;
95 /** The buffer that holds script code. There is always a '\0' after the
96 * current position to prevent reading too far. */
97 char buffer[BUFFER_SIZE];
98 /** The start position within the buffer of the next unread token. */
99 size_t buffer_pos;
100 /** The start position within the buffer of the current token. */
101 size_t old_buffer_pos;
102 /** The line number of the current token. */
103 int lineno;
104 /** Added to a buffer position to find the column number. */
105 int line_offset;
106
107 /** The type of the current token. */
108 token_type_t token;
109 union {
110 /** The value of a TOKEN_IDENTIFIER token. Unless changed to
111 * NULL, it will be freed when the next token is read. */
112 char *token_string;
113 /** The value of a TOKEN_INTEGER token. */
114 bithenge_int_t token_int;
115 };
116
117 /** The names of the current transform's parameters. */
118 char **parameter_names;
119 /** The number of parameters. */
120 int num_params;
121 /** @a parse_expression sets this when TOKEN_IN is used. */
122 bool in_node_used;
123} state_t;
124
125/** Free the previous token's data. This must be called before changing
126 * state->token. */
127static void done_with_token(state_t *state)
128{
129 if (state->token == TOKEN_IDENTIFIER)
130 free(state->token_string);
131 state->token = TOKEN_ERROR;
132}
133
134/** Note that an error has occurred if error is not EOK. */
135static void error_errno(state_t *state, int error)
136{
137 // Don't overwrite a previous error.
138 if (state->error == EOK && error != EOK) {
139 done_with_token(state);
140 state->token = TOKEN_ERROR;
141 state->error = error;
142 }
143}
144
145/** Note that a syntax error has occurred and print an error message. */
146static void syntax_error(state_t *state, const char *message)
147{
148 // Printing multiple errors is confusing.
149 if (state->error == EOK) {
150 size_t start_char = state->old_buffer_pos + state->line_offset;
151 size_t end_char = state->buffer_pos + state->line_offset;
152 size_t size = end_char - start_char;
153 fprintf(stderr, "%s:%d:", state->filename, state->lineno);
154 if (size <= 1)
155 fprintf(stderr, "%zd: ", start_char);
156 else
157 fprintf(stderr, "%zd-%zd: ", start_char, end_char - 1);
158 fprintf(stderr, "%s: \"%.*s\"\n", message, (int)size,
159 state->buffer + state->old_buffer_pos);
160 error_errno(state, EINVAL);
161 }
162}
163
164/** Ensure the buffer contains enough characters to read a token. */
165static void fill_buffer(state_t *state)
166{
167 if (state->buffer_pos + MAX_TOKEN_SIZE < BUFFER_SIZE)
168 return;
169
170 size_t empty_size = state->buffer_pos;
171 size_t used_size = BUFFER_SIZE - 1 - state->buffer_pos;
172 memmove(state->buffer, state->buffer + state->buffer_pos, used_size);
173 state->line_offset += state->buffer_pos;
174 state->buffer_pos = 0;
175
176 size_t read_size = fread(state->buffer + used_size, 1, empty_size,
177 state->file);
178 if (ferror(state->file))
179 error_errno(state, EIO);
180 state->buffer[used_size + read_size] = '\0';
181}
182
183/** Read the next token. */
184static void next_token(state_t *state)
185{
186 fill_buffer(state);
187 done_with_token(state);
188 state->old_buffer_pos = state->buffer_pos;
189 char ch = state->buffer[state->buffer_pos];
190 if (ch == '\0') {
191 state->token = TOKEN_EOF;
192 } else if (ch == '#') {
193 while (state->buffer[state->buffer_pos] != '\n'
194 && state->buffer[state->buffer_pos] != '\0') {
195 state->buffer_pos++;
196 fill_buffer(state);
197 }
198 next_token(state);
199 return;
200 } else if (isspace(ch)) {
201 // Will eventually reach the '\0' at the end
202 while (isspace(state->buffer[state->buffer_pos])) {
203 if (state->buffer[state->buffer_pos] == '\n') {
204 state->lineno++;
205 state->line_offset = -state->buffer_pos;
206 }
207 state->buffer_pos++;
208 }
209 next_token(state);
210 return;
211 } else if (isalpha(ch)) {
212 while (isalnum(state->buffer[state->buffer_pos])
213 || state->buffer[state->buffer_pos] == '_')
214 state->buffer_pos++;
215 char *value = str_ndup(state->buffer + state->old_buffer_pos,
216 state->buffer_pos - state->old_buffer_pos);
217 if (!value) {
218 error_errno(state, ENOMEM);
219 } else if (!str_cmp(value, "do")) {
220 state->token = TOKEN_DO;
221 free(value);
222 } else if (!str_cmp(value, "else")) {
223 state->token = TOKEN_ELSE;
224 free(value);
225 } else if (!str_cmp(value, "false")) {
226 state->token = TOKEN_FALSE;
227 free(value);
228 } else if (!str_cmp(value, "if")) {
229 state->token = TOKEN_IF;
230 free(value);
231 } else if (!str_cmp(value, "in")) {
232 state->token = TOKEN_IN;
233 free(value);
234 } else if (!str_cmp(value, "repeat")) {
235 state->token = TOKEN_REPEAT;
236 free(value);
237 } else if (!str_cmp(value, "struct")) {
238 state->token = TOKEN_STRUCT;
239 free(value);
240 } else if (!str_cmp(value, "switch")) {
241 state->token = TOKEN_SWITCH;
242 free(value);
243 } else if (!str_cmp(value, "transform")) {
244 state->token = TOKEN_TRANSFORM;
245 free(value);
246 } else if (!str_cmp(value, "true")) {
247 state->token = TOKEN_TRUE;
248 free(value);
249 } else if (!str_cmp(value, "while")) {
250 state->token = TOKEN_WHILE;
251 free(value);
252 } else {
253 state->token = TOKEN_IDENTIFIER;
254 state->token_string = value;
255 }
256 } else if (isdigit(ch)) {
257 while (isdigit(state->buffer[state->buffer_pos]))
258 state->buffer_pos++;
259 state->token = TOKEN_INTEGER;
260 int rc = bithenge_parse_int(state->buffer +
261 state->old_buffer_pos, &state->token_int);
262 error_errno(state, rc);
263 } else if (ch == '<') {
264 state->token = ch;
265 state->buffer_pos++;
266 if (state->buffer[state->buffer_pos] == '-') {
267 state->buffer_pos++;
268 state->token = TOKEN_LEFT_ARROW;
269 }
270 } else if (ch == '=') {
271 state->token = ch;
272 state->buffer_pos++;
273 if (state->buffer[state->buffer_pos] == '=') {
274 state->token = TOKEN_EQUALS;
275 state->buffer_pos++;
276 }
277 } else {
278 state->token = ch;
279 state->buffer_pos++;
280 }
281}
282
283/** Allocate memory and handle failure by setting the error in the state. The
284 * caller must check the state for errors before using the return value of this
285 * function. */
286static void *state_malloc(state_t *state, size_t size)
287{
288 if (state->error != EOK)
289 return NULL;
290 void *result = malloc(size);
291 if (result == NULL)
292 error_errno(state, ENOMEM);
293 return result;
294}
295
296/** Reallocate memory and handle failure by setting the error in the state. If
297 * an error occurs, the existing pointer will be returned. */
298static void *state_realloc(state_t *state, void *ptr, size_t size)
299{
300 if (state->error != EOK)
301 return ptr;
302 void *result = realloc(ptr, size);
303 if (result == NULL) {
304 error_errno(state, ENOMEM);
305 return ptr;
306 }
307 return result;
308}
309
310/** Expect and consume a certain token. If the next token is of the wrong type,
311 * an error is caused. */
312static void expect(state_t *state, token_type_t type)
313{
314 if (state->token != type) {
315 syntax_error(state, "unexpected");
316 return;
317 }
318 next_token(state);
319}
320
321/** Expect and consume an identifier token. If the next token is not an
322 * identifier, an error is caused and this function returns null. */
323static char *expect_identifier(state_t *state)
324{
325 if (state->token != TOKEN_IDENTIFIER) {
326 syntax_error(state, "unexpected (identifier expected)");
327 return NULL;
328 }
329 char *val = state->token_string;
330 state->token_string = NULL;
331 next_token(state);
332 return val;
333}
334
335/** Find a transform by name. A reference will be added to the transform.
336 * @return The found transform, or NULL if none was found. */
337static bithenge_transform_t *get_named_transform(state_t *state,
338 const char *name)
339{
340 for (transform_list_t *e = state->transform_list; e; e = e->next) {
341 if (!str_cmp(e->name, name)) {
342 bithenge_transform_inc_ref(e->transform);
343 return e->transform;
344 }
345 }
346 for (int i = 0; bithenge_primitive_transforms[i].name; i++) {
347 if (!str_cmp(bithenge_primitive_transforms[i].name, name)) {
348 bithenge_transform_t *xform =
349 bithenge_primitive_transforms[i].transform;
350 bithenge_transform_inc_ref(xform);
351 return xform;
352 }
353 }
354 return NULL;
355}
356
357/** Add a named transform. This function takes ownership of the name and a
358 * reference to the transform. If an error has occurred, either may be null. */
359static void add_named_transform(state_t *state, bithenge_transform_t *xform, char *name)
360{
361 transform_list_t *entry = state_malloc(state, sizeof(*entry));
362 if (state->error != EOK) {
363 free(name);
364 bithenge_transform_dec_ref(xform);
365 free(entry);
366 return;
367 }
368 entry->name = name;
369 entry->transform = xform;
370 entry->next = state->transform_list;
371 state->transform_list = entry;
372}
373
374static bithenge_transform_t *parse_transform(state_t *state);
375static bithenge_transform_t *parse_struct(state_t *state);
376static bithenge_expression_t *parse_expression(state_t *state);
377
378
379
380/***************** Expressions *****************/
381
382typedef enum {
383 PRECEDENCE_NONE,
384 PRECEDENCE_EQUALS,
385 PRECEDENCE_ADD,
386 PRECEDENCE_MULTIPLY,
387} precedence_t;
388
389static bithenge_binary_op_t token_as_binary_operator(token_type_t token)
390{
391 switch ((int)token) {
392 case '+':
393 return BITHENGE_EXPRESSION_ADD;
394 case '-':
395 return BITHENGE_EXPRESSION_SUBTRACT;
396 case '*':
397 return BITHENGE_EXPRESSION_MULTIPLY;
398 case TOKEN_EQUALS:
399 return BITHENGE_EXPRESSION_EQUALS;
400 default:
401 return BITHENGE_EXPRESSION_INVALID_BINARY_OP;
402 }
403}
404
405static precedence_t binary_operator_precedence(bithenge_binary_op_t op)
406{
407 switch (op) {
408 case BITHENGE_EXPRESSION_ADD: /* fallthrough */
409 case BITHENGE_EXPRESSION_SUBTRACT:
410 return PRECEDENCE_ADD;
411 case BITHENGE_EXPRESSION_MULTIPLY:
412 return PRECEDENCE_MULTIPLY;
413 case BITHENGE_EXPRESSION_EQUALS:
414 return PRECEDENCE_EQUALS;
415 default:
416 assert(false);
417 return PRECEDENCE_NONE;
418 }
419}
420
421static bithenge_expression_t *parse_term(state_t *state)
422{
423 int rc;
424 if (state->token == TOKEN_TRUE || state->token == TOKEN_FALSE) {
425 bool val = state->token == TOKEN_TRUE;
426 next_token(state);
427 bithenge_node_t *node;
428 rc = bithenge_new_boolean_node(&node, val);
429 if (rc != EOK) {
430 error_errno(state, rc);
431 return NULL;
432 }
433
434 bithenge_expression_t *expr;
435 rc = bithenge_const_expression(&expr, node);
436 if (rc != EOK) {
437 error_errno(state, rc);
438 return NULL;
439 }
440
441 return expr;
442 } else if (state->token == TOKEN_IN) {
443 next_token(state);
444 state->in_node_used = true;
445 bithenge_expression_t *expr;
446 rc = bithenge_in_node_expression(&expr);
447 if (rc != EOK) {
448 error_errno(state, rc);
449 return NULL;
450 }
451 return expr;
452 } else if (state->token == TOKEN_INTEGER) {
453 bithenge_int_t val = state->token_int;
454 next_token(state);
455 bithenge_node_t *node;
456 rc = bithenge_new_integer_node(&node, val);
457 if (rc != EOK) {
458 error_errno(state, rc);
459 return NULL;
460 }
461
462 bithenge_expression_t *expr;
463 rc = bithenge_const_expression(&expr, node);
464 if (rc != EOK) {
465 error_errno(state, rc);
466 return NULL;
467 }
468
469 return expr;
470 } else if (state->token == TOKEN_IDENTIFIER) {
471 int i;
472 for (i = 0; i < state->num_params; i++)
473 if (!str_cmp(state->parameter_names[i],
474 state->token_string))
475 break;
476
477 if (i == state->num_params) {
478 syntax_error(state, "unknown identifier");
479 return NULL;
480 }
481
482 bithenge_expression_t *expr;
483 rc = bithenge_param_expression(&expr, i);
484 if (rc != EOK) {
485 error_errno(state, rc);
486 return NULL;
487 }
488
489 next_token(state);
490
491 return expr;
492 } else if (state->token == '.') {
493 next_token(state);
494
495 const char *id = expect_identifier(state);
496 bithenge_node_t *key = NULL;
497 bithenge_expression_t *expr = NULL;
498
499 if (state->error == EOK) {
500 rc = bithenge_new_string_node(&key, id, true);
501 id = NULL;
502 error_errno(state, rc);
503 }
504
505 if (state->error == EOK) {
506 rc = bithenge_scope_member_expression(&expr, key);
507 key = NULL;
508 if (rc != EOK)
509 expr = NULL;
510 error_errno(state, rc);
511 }
512
513 if (state->error != EOK) {
514 free((char *)id);
515 bithenge_node_dec_ref(key);
516 bithenge_expression_dec_ref(expr);
517 return NULL;
518 }
519
520 return expr;
521 } else if (state->token == '(') {
522 next_token(state);
523 bithenge_expression_t *expr = parse_expression(state);
524 expect(state, ')');
525 return expr;
526 } else {
527 syntax_error(state, "expression expected");
528 return NULL;
529 }
530}
531
532static bithenge_expression_t *parse_expression_precedence(state_t *state,
533 precedence_t prev_precedence)
534{
535 bithenge_expression_t *expr = parse_term(state);
536 while (state->error == EOK) {
537 bithenge_binary_op_t op =
538 token_as_binary_operator(state->token);
539 if (op == BITHENGE_EXPRESSION_INVALID_BINARY_OP)
540 break;
541 precedence_t precedence = binary_operator_precedence(op);
542 if (precedence <= prev_precedence)
543 break;
544 next_token(state);
545
546 bithenge_expression_t *expr2 = parse_term(state);
547 if (state->error != EOK) {
548 bithenge_expression_dec_ref(expr2);
549 break;
550 }
551 int rc = bithenge_binary_expression(&expr, op, expr, expr2);
552 if (rc != EOK)
553 error_errno(state, rc);
554 }
555 if (state->error != EOK) {
556 bithenge_expression_dec_ref(expr);
557 expr = NULL;
558 }
559 return expr;
560}
561
562static bithenge_expression_t *parse_expression(state_t *state)
563{
564 return parse_expression_precedence(state, PRECEDENCE_NONE);
565}
566
567
568
569/* state->token must be TOKEN_IDENTIFIER when this is called. */
570static bithenge_transform_t *parse_invocation(state_t *state)
571{
572 bithenge_transform_t *result = get_named_transform(state,
573 state->token_string);
574 if (!result)
575 syntax_error(state, "transform not found");
576 next_token(state);
577
578 bithenge_expression_t **params = NULL;
579 int num_params = 0;
580 if (state->token == '(') {
581 next_token(state);
582 while (state->error == EOK && state->token != ')') {
583 if (num_params)
584 expect(state, ',');
585 params = state_realloc(state, params,
586 (num_params + 1)*sizeof(*params));
587 if (state->error != EOK)
588 break;
589 params[num_params] = parse_expression(state);
590 num_params++;
591 }
592 expect(state, ')');
593 }
594
595 /* TODO: show correct error position */
596 if (state->error == EOK
597 && bithenge_transform_num_params(result) != num_params)
598 syntax_error(state, "incorrect number of parameters before");
599
600 if (state->error != EOK) {
601 while (num_params--)
602 bithenge_expression_dec_ref(params[num_params]);
603 free(params);
604 bithenge_transform_dec_ref(result);
605 return NULL;
606 }
607
608 if (num_params) {
609 int rc = bithenge_param_wrapper(&result, result, params);
610 if (rc != EOK) {
611 error_errno(state, rc);
612 result = NULL;
613 }
614 }
615
616 return result;
617}
618
619/** Create a transform that just produces an empty node.
620 * @param state The parser state.
621 * @return The new transform, or NULL on error. */
622static bithenge_transform_t *make_empty_transform(state_t *state)
623{
624 bithenge_node_t *node;
625 int rc = bithenge_new_empty_internal_node(&node);
626 if (rc != EOK) {
627 error_errno(state, rc);
628 return NULL;
629 }
630
631 bithenge_expression_t *expr;
632 rc = bithenge_const_expression(&expr, node);
633 if (rc != EOK) {
634 error_errno(state, rc);
635 return NULL;
636 }
637
638 bithenge_transform_t *xform;
639 rc = bithenge_inputless_transform(&xform, expr);
640 if (rc != EOK) {
641 error_errno(state, rc);
642 return NULL;
643 }
644
645 return xform;
646}
647
648static bithenge_transform_t *parse_if(state_t *state, bool in_struct)
649{
650 expect(state, TOKEN_IF);
651 expect(state, '(');
652 bithenge_expression_t *expr = parse_expression(state);
653 expect(state, ')');
654 expect(state, '{');
655 bithenge_transform_t *true_xform =
656 in_struct ? parse_struct(state) : parse_transform(state);
657 expect(state, '}');
658
659 bithenge_transform_t *false_xform = NULL;
660 if (state->token == TOKEN_ELSE) {
661 next_token(state);
662 expect(state, '{');
663 false_xform =
664 in_struct ? parse_struct(state) : parse_transform(state);
665 expect(state, '}');
666 } else {
667 if (in_struct)
668 false_xform = make_empty_transform(state);
669 else
670 syntax_error(state, "else expected");
671 }
672
673 if (state->error != EOK) {
674 bithenge_expression_dec_ref(expr);
675 bithenge_transform_dec_ref(true_xform);
676 bithenge_transform_dec_ref(false_xform);
677 return NULL;
678 }
679
680 bithenge_transform_t *if_xform;
681 int rc = bithenge_if_transform(&if_xform, expr, true_xform,
682 false_xform);
683 if (rc != EOK) {
684 error_errno(state, rc);
685 return NULL;
686 }
687 return if_xform;
688}
689
690static bithenge_transform_t *parse_switch(state_t *state, bool in_struct)
691{
692 expect(state, TOKEN_SWITCH);
693 expect(state, '(');
694 bithenge_expression_t *ref_expr = parse_expression(state);
695 expect(state, ')');
696 expect(state, '{');
697 int num = 0;
698 bithenge_expression_t **exprs = NULL;
699 bithenge_transform_t **xforms = NULL;
700 while (state->error == EOK && state->token != '}') {
701 bithenge_expression_t *expr;
702 if (state->token == TOKEN_ELSE) {
703 next_token(state);
704 bithenge_node_t *node;
705 int rc = bithenge_new_boolean_node(&node, true);
706 if (rc != EOK) {
707 error_errno(state, rc);
708 break;
709 }
710 rc = bithenge_const_expression(&expr, node);
711 if (rc != EOK) {
712 error_errno(state, rc);
713 break;
714 }
715 } else {
716 expr = parse_expression(state);
717 if (state->error == EOK) {
718 bithenge_expression_inc_ref(ref_expr);
719 int rc = bithenge_binary_expression(&expr,
720 BITHENGE_EXPRESSION_EQUALS, ref_expr,
721 expr);
722 if (rc != EOK) {
723 error_errno(state, rc);
724 break;
725 }
726 }
727 }
728
729 expect(state, ':');
730 bithenge_transform_t *xform;
731 if (in_struct) {
732 expect(state, '{');
733 xform = parse_struct(state);
734 expect(state, '}');
735 } else
736 xform = parse_transform(state);
737 expect(state, ';');
738
739 exprs = state_realloc(state, exprs,
740 sizeof(*exprs) * (num + 1));
741 xforms = state_realloc(state, xforms,
742 sizeof(*xforms) * (num + 1));
743 if (state->error != EOK) {
744 bithenge_expression_dec_ref(expr);
745 bithenge_transform_dec_ref(xform);
746 break;
747 }
748
749 exprs[num] = expr;
750 xforms[num] = xform;
751 num++;
752 }
753 bithenge_expression_dec_ref(ref_expr);
754
755 bithenge_transform_t *switch_xform = &bithenge_invalid_transform;
756 bithenge_transform_inc_ref(switch_xform);
757 while (state->error == EOK && num >= 1) {
758 num--;
759 int rc = bithenge_if_transform(&switch_xform, exprs[num],
760 xforms[num], switch_xform);
761 if (rc != EOK)
762 error_errno(state, rc);
763 }
764
765 while (num >= 1) {
766 num--;
767 bithenge_expression_dec_ref(exprs[num]);
768 bithenge_transform_dec_ref(xforms[num]);
769 }
770 free(exprs);
771 free(xforms);
772
773 expect(state, '}');
774 return switch_xform;
775}
776
777static bithenge_transform_t *parse_repeat(state_t *state)
778{
779 expect(state, TOKEN_REPEAT);
780 bithenge_expression_t *expr = NULL;
781 if (state->token == '(') {
782 next_token(state);
783 expr = parse_expression(state);
784 expect(state, ')');
785 }
786 expect(state, '{');
787 bithenge_transform_t *xform = parse_transform(state);
788 expect(state, '}');
789
790 if (state->error != EOK) {
791 bithenge_expression_dec_ref(expr);
792 bithenge_transform_dec_ref(xform);
793 return NULL;
794 }
795
796 bithenge_transform_t *repeat_xform;
797 int rc = bithenge_repeat_transform(&repeat_xform, xform, expr);
798 if (rc != EOK) {
799 error_errno(state, rc);
800 return NULL;
801 }
802 return repeat_xform;
803}
804
805static bithenge_transform_t *parse_do_while(state_t *state)
806{
807 expect(state, TOKEN_DO);
808 expect(state, '{');
809 bithenge_transform_t *xform = parse_transform(state);
810 expect(state, '}');
811 expect(state, TOKEN_WHILE);
812 expect(state, '(');
813 bithenge_expression_t *expr = parse_expression(state);
814 expect(state, ')');
815
816 if (state->error != EOK) {
817 bithenge_expression_dec_ref(expr);
818 bithenge_transform_dec_ref(xform);
819 return NULL;
820 }
821
822 bithenge_transform_t *do_while_xform;
823 int rc = bithenge_do_while_transform(&do_while_xform, xform, expr);
824 if (rc != EOK) {
825 error_errno(state, rc);
826 return NULL;
827 }
828 return do_while_xform;
829}
830
831/* The TOKEN_STRUCT and '{' must already have been skipped. */
832static bithenge_transform_t *parse_struct(state_t *state)
833{
834 size_t num = 0;
835 bithenge_named_transform_t *subxforms;
836 /* We keep an extra space for the {NULL, NULL} terminator. */
837 subxforms = state_malloc(state, sizeof(*subxforms));
838 while (state->error == EOK && state->token != '}') {
839 if (state->token == TOKEN_IF) {
840 subxforms[num].transform = parse_if(state, true);
841 subxforms[num].name = NULL;
842 } else if (state->token == TOKEN_SWITCH) {
843 subxforms[num].transform = parse_switch(state, true);
844 subxforms[num].name = NULL;
845 } else {
846 if (state->token == '.') {
847 next_token(state);
848 subxforms[num].name = expect_identifier(state);
849 } else {
850 subxforms[num].name = NULL;
851 }
852 expect(state, TOKEN_LEFT_ARROW);
853 subxforms[num].transform = parse_transform(state);
854 expect(state, ';');
855 }
856 num++;
857 subxforms = state_realloc(state, subxforms,
858 (num + 1)*sizeof(*subxforms));
859 }
860
861 if (state->error != EOK) {
862 while (num--) {
863 free((void *)subxforms[num].name);
864 bithenge_transform_dec_ref(subxforms[num].transform);
865 }
866 free(subxforms);
867 return NULL;
868 }
869
870 subxforms[num].name = NULL;
871 subxforms[num].transform = NULL;
872 bithenge_transform_t *result;
873 int rc = bithenge_new_struct(&result, subxforms);
874 if (rc != EOK) {
875 error_errno(state, rc);
876 return NULL;
877 }
878 return result;
879}
880
881/** Parse a transform without composition.
882 * @return The parsed transform, or NULL if an error occurred. */
883static bithenge_transform_t *parse_transform_no_compose(state_t *state)
884{
885 if (state->token == '(') {
886 next_token(state);
887 state->in_node_used = false;
888 bithenge_expression_t *expr = parse_expression(state);
889 expect(state, ')');
890 if (state->error != EOK) {
891 bithenge_expression_dec_ref(expr);
892 return NULL;
893 }
894
895 bithenge_transform_t *xform;
896 int rc;
897 if (state->in_node_used)
898 rc = bithenge_expression_transform(&xform, expr);
899 else
900 rc = bithenge_inputless_transform(&xform, expr);
901 if (rc != EOK) {
902 error_errno(state, rc);
903 return NULL;
904 }
905 return xform;
906 } else if (state->token == TOKEN_DO) {
907 return parse_do_while(state);
908 } else if (state->token == TOKEN_IDENTIFIER) {
909 return parse_invocation(state);
910 } else if (state->token == TOKEN_IF) {
911 return parse_if(state, false);
912 } else if (state->token == TOKEN_REPEAT) {
913 return parse_repeat(state);
914 } else if (state->token == TOKEN_STRUCT) {
915 next_token(state);
916 expect(state, '{');
917 bithenge_transform_t *xform = parse_struct(state);
918 expect(state, '}');
919 return xform;
920 } else if (state->token == TOKEN_SWITCH) {
921 return parse_switch(state, false);
922 } else {
923 syntax_error(state, "unexpected (transform expected)");
924 return NULL;
925 }
926}
927
928/** Parse a transform.
929 * @return The parsed transform, or NULL if an error occurred. */
930static bithenge_transform_t *parse_transform(state_t *state)
931{
932 bithenge_transform_t *result = parse_transform_no_compose(state);
933 bithenge_transform_t **xforms = NULL;
934 size_t num = 1;
935 while (state->token == TOKEN_LEFT_ARROW) {
936 expect(state, TOKEN_LEFT_ARROW);
937 xforms = state_realloc(state, xforms,
938 (num + 1) * sizeof(*xforms));
939 if (state->error != EOK)
940 break;
941 xforms[num] = parse_transform_no_compose(state);
942 num++;
943 }
944 if (state->error != EOK) {
945 while (xforms && num--)
946 bithenge_transform_dec_ref(xforms[num]);
947 free(xforms);
948 bithenge_transform_dec_ref(result);
949 return NULL;
950 }
951 if (xforms) {
952 xforms[0] = result;
953 int rc = bithenge_new_composed_transform(&result, xforms, num);
954 if (rc != EOK) {
955 error_errno(state, rc);
956 return NULL;
957 }
958 }
959 return result;
960}
961
962/** Parse a definition. */
963static void parse_definition(state_t *state)
964{
965 expect(state, TOKEN_TRANSFORM);
966 char *name = expect_identifier(state);
967
968 if (state->token == '(') {
969 next_token(state);
970 while (state->error == EOK && state->token != ')') {
971 if (state->num_params)
972 expect(state, ',');
973 state->parameter_names = state_realloc(state,
974 state->parameter_names,
975 (state->num_params + 1)*sizeof(*state->parameter_names));
976 if (state->error != EOK)
977 break;
978 state->parameter_names[state->num_params++] =
979 expect_identifier(state);
980 }
981 expect(state, ')');
982 }
983
984 expect(state, '=');
985 bithenge_transform_t *xform = parse_transform(state);
986 expect(state, ';');
987
988 if (state->error == EOK) {
989 int rc = bithenge_new_barrier_transform(&xform, xform,
990 state->num_params);
991 if (rc != EOK) {
992 xform = NULL;
993 error_errno(state, rc);
994 }
995 }
996
997 add_named_transform(state, xform, name);
998
999 for (int i = 0; i < state->num_params; i++)
1000 free(state->parameter_names[i]);
1001 free(state->parameter_names);
1002 state->parameter_names = NULL;
1003 state->num_params = 0;
1004}
1005
1006/** Initialize the state. */
1007static void state_init(state_t *state, const char *filename)
1008{
1009 state->error = EOK;
1010 state->transform_list = NULL;
1011 state->parameter_names = NULL;
1012 state->num_params = 0;
1013 state->token = TOKEN_ERROR;
1014 state->old_buffer_pos = state->buffer_pos = BUFFER_SIZE - 1;
1015 state->lineno = 1;
1016 state->line_offset = (int)-state->buffer_pos + 1;
1017 state->filename = filename;
1018 state->file = fopen(filename, "r");
1019 if (!state->file) {
1020 error_errno(state, errno);
1021 } else {
1022 next_token(state);
1023 }
1024}
1025
1026/** Destroy the state. */
1027static void state_destroy(state_t *state)
1028{
1029 done_with_token(state);
1030 state->token = TOKEN_ERROR;
1031 if (state->file)
1032 fclose(state->file);
1033 transform_list_t *entry = state->transform_list;
1034 while (entry) {
1035 transform_list_t *next = entry->next;
1036 free(entry->name);
1037 bithenge_transform_dec_ref(entry->transform);
1038 free(entry);
1039 entry = next;
1040 }
1041 for (int i = 0; i < state->num_params; i++)
1042 free(state->parameter_names[i]);
1043 free(state->parameter_names);
1044}
1045
1046/** Parse a script file.
1047 * @param filename The name of the script file.
1048 * @param[out] out Stores the "main" transform.
1049 * @return EOK on success, EINVAL on syntax error, or an error code from
1050 * errno.h. */
1051int bithenge_parse_script(const char *filename, bithenge_transform_t **out)
1052{
1053 state_t state;
1054 state_init(&state, filename);
1055 while (state.error == EOK && state.token != TOKEN_EOF)
1056 parse_definition(&state);
1057 *out = get_named_transform(&state, "main");
1058 int rc = state.error;
1059 state_destroy(&state);
1060 if (rc == EOK && !*out) {
1061 fprintf(stderr, "no \"main\" transform\n");
1062 rc = EINVAL;
1063 }
1064 return rc;
1065}
1066
1067/** @}
1068 */
Note: See TracBrowser for help on using the repository browser.