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

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

Bithenge: make each transform definition have its own scope

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