Theme Editor: Factored out code to skip over enum/arg lists while scanning for childr...
[kugel-rb.git] / utils / themeeditor / skin_parser.c
blob8cf23bd11aaf3a0b28348f434f6ef48f8b1392e4
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2010 Robert Bieber
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <ctype.h>
27 #include "skin_parser.h"
28 #include "skin_debug.h"
29 #include "tag_table.h"
30 #include "symbols.h"
31 #include "skin_scan.h"
33 /* Declaration of parse tree buffer */
34 char skin_parse_tree[SKIN_MAX_MEMORY];
35 int skin_current_block = 0;
37 /* Global variables for the parser */
38 int skin_line = 0;
40 /* Auxiliary parsing functions (not visible at global scope) */
41 struct skin_element* skin_parse_viewport(char** document);
42 struct skin_element* skin_parse_line(char** document);
43 struct skin_element* skin_parse_line_optional(char** document, int conditional);
44 struct skin_element* skin_parse_sublines(char** document);
45 struct skin_element* skin_parse_sublines_optional(char** document,
46 int conditional);
48 int skin_parse_tag(struct skin_element* element, char** document);
49 int skin_parse_text(struct skin_element* element, char** document,
50 int conditional);
51 int skin_parse_conditional(struct skin_element* element, char** document);
52 int skin_parse_comment(struct skin_element* element, char** document);
53 struct skin_element* skin_parse_code_as_arg(char** document);
55 struct skin_element* skin_parse(const char* document)
58 struct skin_element* root = NULL;
59 struct skin_element* last = NULL;
61 struct skin_element** to_write = 0;
63 char* cursor = (char*)document; /*Keeps track of location in the document*/
65 skin_line = 1;
67 skin_clear_errors();
69 while(*cursor != '\0')
72 if(!root)
73 to_write = &root;
74 else
75 to_write = &(last->next);
78 *to_write = skin_parse_viewport(&cursor);
79 last = *to_write;
80 if(!last)
81 return NULL;
83 /* Making sure last is at the end */
84 while(last->next)
85 last = last->next;
89 return root;
93 struct skin_element* skin_parse_viewport(char** document)
96 struct skin_element* root = NULL;
97 struct skin_element* last = NULL;
98 struct skin_element* retval = NULL;
100 retval = skin_alloc_element();
101 retval->type = VIEWPORT;
102 retval->children_count = 1;
103 retval->line = skin_line;
105 struct skin_element** to_write = 0;
107 char* cursor = *document; /* Keeps track of location in the document */
108 char* bookmark; /* Used when we need to look ahead */
110 int sublines = 0; /* Flag for parsing sublines */
112 /* Parsing out the viewport tag if there is one */
113 if(check_viewport(cursor))
115 retval->children_count = 2;
116 retval->children = skin_alloc_children(2);
117 retval->children[0] = skin_alloc_element();
118 skin_parse_tag(retval->children[0], &cursor);
119 if(*cursor == '\n')
121 cursor++;
122 skin_line++;
125 else
127 retval->children_count = 1;
128 retval->children = skin_alloc_children(1);
132 while(*cursor != '\0' && !(check_viewport(cursor) && cursor != *document))
135 /* First, we check to see if this line will contain sublines */
136 bookmark = cursor;
137 sublines = 0;
138 while(*cursor != '\n' && *cursor != '\0'
139 && !(check_viewport(cursor) && cursor != *document))
141 if(*cursor == MULTILINESYM)
143 sublines = 1;
144 break;
146 else if(*cursor == TAGSYM)
148 /* A ';' directly after a '%' doesn't count */
149 cursor ++;
151 if(*cursor == '\0')
152 break;
154 cursor++;
156 else if(*cursor == COMMENTSYM)
158 skip_comment(&cursor);
160 else if(*cursor == ARGLISTOPENSYM)
162 skip_arglist(&cursor);
164 else if(*cursor == ENUMLISTOPENSYM)
166 skip_arglist(&cursor);
168 else
170 /* Advancing the cursor as normal */
171 cursor++;
174 cursor = bookmark;
176 if(!root)
177 to_write = &root;
178 else
179 to_write = &(last->next);
181 if(sublines)
183 *to_write = skin_parse_sublines(&cursor);
184 last = *to_write;
185 if(!last)
186 return NULL;
188 else
191 *to_write = skin_parse_line(&cursor);
192 last = *to_write;
193 if(!last)
194 return NULL;
198 /* Making sure last is at the end */
199 while(last->next)
200 last = last->next;
202 if(*cursor == '\n')
204 cursor++;
205 skin_line++;
209 *document = cursor;
211 retval->children[retval->children_count - 1] = root;
212 return retval;
216 /* Auxiliary Parsing Functions */
218 struct skin_element* skin_parse_line(char**document)
221 return skin_parse_line_optional(document, 0);
227 * If conditional is set to true, then this will break upon encountering
228 * SEPERATESYM. This should only be used when parsing a line inside a
229 * conditional, otherwise just use the wrapper function skin_parse_line()
231 struct skin_element* skin_parse_line_optional(char** document, int conditional)
233 char* cursor = *document;
235 struct skin_element* root = NULL;
236 struct skin_element* current = NULL;
237 struct skin_element* retval = NULL;
239 /* A wrapper for the line */
240 retval = skin_alloc_element();
241 retval->type = LINE;
242 retval->line = skin_line;
243 retval->children_count = 1;
244 retval->children = skin_alloc_children(1);
246 while(*cursor != '\n' && *cursor != '\0' && *cursor != MULTILINESYM
247 && !((*cursor == ARGLISTSEPERATESYM
248 || *cursor == ARGLISTCLOSESYM
249 || *cursor == ENUMLISTSEPERATESYM
250 || *cursor == ENUMLISTCLOSESYM)
251 && conditional)
252 && !(check_viewport(cursor) && cursor != *document))
254 /* Allocating memory if necessary */
255 if(root)
257 current->next = skin_alloc_element();
258 current = current->next;
260 else
262 current = skin_alloc_element();
263 root = current;
266 /* Parsing the current element */
267 if(*cursor == TAGSYM && cursor[1] == CONDITIONSYM)
269 if(!skin_parse_conditional(current, &cursor))
270 return NULL;
272 else if(*cursor == TAGSYM && !find_escape_character(cursor[1]))
274 if(!skin_parse_tag(current, &cursor))
275 return NULL;
277 else if(*cursor == COMMENTSYM)
279 if(!skin_parse_comment(current, &cursor))
280 return NULL;
282 else
284 if(!skin_parse_text(current, &cursor, conditional))
285 return NULL;
289 /* Moving up the calling function's pointer */
290 *document = cursor;
292 retval->children[0] = root;
293 return retval;
296 struct skin_element* skin_parse_sublines(char** document)
298 return skin_parse_sublines_optional(document, 0);
301 struct skin_element* skin_parse_sublines_optional(char** document,
302 int conditional)
304 struct skin_element* retval;
305 char* cursor = *document;
306 int sublines = 1;
307 int i;
309 retval = skin_alloc_element();
310 retval->type = SUBLINES;
311 retval->next = NULL;
312 retval->line = skin_line;
314 /* First we count the sublines */
315 while(*cursor != '\0' && *cursor != '\n'
316 && !((*cursor == ARGLISTSEPERATESYM
317 || *cursor == ARGLISTCLOSESYM
318 || *cursor == ENUMLISTSEPERATESYM
319 || *cursor == ENUMLISTCLOSESYM)
320 && conditional)
321 && !(check_viewport(cursor) && cursor != *document))
323 if(*cursor == COMMENTSYM)
325 skip_comment(&cursor);
327 else if(*cursor == ENUMLISTOPENSYM)
329 skip_enumlist(&cursor);
331 else if(*cursor == ARGLISTOPENSYM)
333 skip_arglist(&cursor);
335 else if(*cursor == TAGSYM)
337 cursor++;
338 if(*cursor == '\0' || *cursor == '\n')
339 break;
340 cursor++;
342 else if(*cursor == MULTILINESYM)
344 sublines++;
345 cursor++;
347 else
349 cursor++;
353 /* ...and then we parse them */
354 retval->children_count = sublines;
355 retval->children = skin_alloc_children(sublines);
357 cursor = *document;
358 for(i = 0; i < sublines; i++)
360 retval->children[i] = skin_parse_line_optional(&cursor, conditional);
361 skip_whitespace(&cursor);
363 if(*cursor != MULTILINESYM && i != sublines - 1)
365 skin_error(MULTILINE_EXPECTED);
366 return NULL;
368 else if(i != sublines - 1)
370 cursor++;
374 *document = cursor;
376 return retval;
379 int skin_parse_tag(struct skin_element* element, char** document)
382 char* cursor = *document + 1;
383 char* bookmark;
385 char tag_name[3];
386 char* tag_args;
387 struct tag_info *tag;
389 int num_args = 1;
390 int i;
391 int star = 0; /* Flag for the all-or-none option */
392 int req_args; /* To mark when we enter optional arguments */
394 int optional = 0;
396 /* Checking the tag name */
397 tag_name[0] = cursor[0];
398 tag_name[1] = cursor[1];
399 tag_name[2] = '\0';
401 /* First we check the two characters after the '%', then a single char */
402 tag = find_tag(tag_name);
404 if(!tag)
406 tag_name[1] = '\0';
407 tag = find_tag(tag_name);
408 cursor++;
410 else
412 cursor += 2;
415 if(!tag)
417 skin_error(ILLEGAL_TAG);
418 return 0;
421 /* Copying basic tag info */
422 element->type = TAG;
423 element->tag = tag;
424 tag_args = tag->params;
425 element->line = skin_line;
427 /* Checking for the * flag */
428 if(tag_args[0] == '*')
430 star = 1;
431 tag_args++;
434 /* If this tag has no arguments, we can bail out now */
435 if(strlen(tag_args) == 0
436 || (tag_args[0] == '|' && *cursor != ARGLISTOPENSYM)
437 || (star && *cursor != ARGLISTOPENSYM))
439 *document = cursor;
440 return 1;
443 /* Checking the number of arguments and allocating args */
444 if(*cursor != ARGLISTOPENSYM && tag_args[0] != '|')
446 skin_error(ARGLIST_EXPECTED);
447 return 0;
449 else
451 cursor++;
454 bookmark = cursor;
455 while(*cursor != '\n' && *cursor != '\0' && *cursor != ARGLISTCLOSESYM)
457 /* Skipping over escaped characters */
458 if(*cursor == TAGSYM)
460 cursor++;
461 if(*cursor == '\0')
462 break;
463 cursor++;
465 else if(*cursor == COMMENTSYM)
467 skip_comment(&cursor);
469 else if(*cursor == ARGLISTOPENSYM)
471 skip_arglist(&cursor);
473 else if(*cursor == ARGLISTSEPERATESYM)
475 num_args++;
476 cursor++;
478 else
480 cursor++;
484 cursor = bookmark; /* Restoring the cursor */
485 element->params_count = num_args;
486 element->params = skin_alloc_params(num_args);
488 /* Now we have to actually parse each argument */
489 for(i = 0; i < num_args; i++)
491 /* Making sure we haven't run out of arguments */
492 if(*tag_args == '\0')
494 skin_error(TOO_MANY_ARGS);
495 return 0;
498 /* Checking for the optional bar */
499 if(*tag_args == '|')
501 optional = 1;
502 req_args = i;
503 tag_args++;
506 /* Scanning the arguments */
507 skip_whitespace(&cursor);
510 /* Checking for comments */
511 if(*cursor == COMMENTSYM)
512 skip_comment(&cursor);
514 /* Storing the type code */
515 element->params[i].type_code = *tag_args;
517 /* Checking a nullable argument for null */
518 if(*cursor == DEFAULTSYM && !isdigit(cursor[1]))
520 if(islower(*tag_args))
522 element->params[i].type = DEFAULT;
523 cursor++;
525 else
527 skin_error(DEFAULT_NOT_ALLOWED);
528 return 0;
531 else if(tolower(*tag_args) == 'i')
533 /* Scanning an int argument */
534 if(!isdigit(*cursor) && *cursor != '-')
536 skin_error(INT_EXPECTED);
537 return 0;
540 element->params[i].type = NUMERIC;
541 element->params[i].data.numeric = scan_int(&cursor);
543 else if(tolower(*tag_args) == 's' || tolower(*tag_args) == 'f')
545 /* Scanning a string argument */
546 element->params[i].type = STRING;
547 element->params[i].data.text = scan_string(&cursor);
550 else if(tolower(*tag_args) == 'c')
552 /* Recursively parsing a code argument */
553 element->params[i].type = CODE;
554 element->params[i].data.code = skin_parse_code_as_arg(&cursor);
555 if(!element->params[i].data.code)
556 return 0;
559 skip_whitespace(&cursor);
561 if(*cursor != ARGLISTSEPERATESYM && i < num_args - 1)
563 skin_error(SEPERATOR_EXPECTED);
564 return 0;
566 else if(*cursor != ARGLISTCLOSESYM && i == num_args - 1)
568 skin_error(CLOSE_EXPECTED);
569 return 0;
571 else
573 cursor++;
576 tag_args++;
578 /* Checking for the optional bar */
579 if(*tag_args == '|')
581 optional = 1;
582 req_args = i + 1;
583 tag_args++;
588 /* Checking for a premature end */
589 if(*tag_args != '\0' && !optional)
591 skin_error(INSUFFICIENT_ARGS);
592 return 0;
595 *document = cursor;
597 return 1;
601 * If the conditional flag is set true, then parsing text will stop at an
602 * ARGLISTSEPERATESYM. Only set that flag when parsing within a conditional
604 int skin_parse_text(struct skin_element* element, char** document,
605 int conditional)
607 char* cursor = *document;
609 int length = 0;
611 int dest;
613 /* First figure out how much text we're copying */
614 while(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
615 && *cursor != COMMENTSYM
616 && !((*cursor == ARGLISTSEPERATESYM
617 || *cursor == ARGLISTCLOSESYM
618 || *cursor == ENUMLISTSEPERATESYM
619 || *cursor == ENUMLISTCLOSESYM)
620 && conditional))
622 /* Dealing with possibility of escaped characters */
623 if(*cursor == TAGSYM)
625 if(find_escape_character(cursor[1]))
626 cursor++;
627 else
628 break;
631 length++;
632 cursor++;
635 cursor = *document;
637 /* Copying the text into the element struct */
638 element->type = TEXT;
639 element->line = skin_line;
640 element->next = NULL;
641 element->text = skin_alloc_string(length);
643 for(dest = 0; dest < length; dest++)
645 /* Advancing cursor if we've encountered an escaped character */
646 if(*cursor == TAGSYM)
647 cursor++;
649 element->text[dest] = *cursor;
650 cursor++;
652 element->text[length] = '\0';
654 *document = cursor;
656 return 1;
659 int skin_parse_conditional(struct skin_element* element, char** document)
662 char* cursor = *document + 1; /* Starting past the "%" */
663 char* bookmark;
664 struct skin_element* tag = skin_alloc_element(); /* The tag to evaluate */
665 int children = 1;
666 int i;
668 element->type = CONDITIONAL;
669 element->line = skin_line;
671 /* Parsing the tag first */
672 if(!skin_parse_tag(tag, &cursor))
673 return 0;
675 /* Counting the children */
676 if(*(cursor++) != ENUMLISTOPENSYM)
678 skin_error(ARGLIST_EXPECTED);
679 return 0;
681 bookmark = cursor;
682 while(*cursor != ENUMLISTCLOSESYM && *cursor != '\n' && *cursor != '\0')
684 if(*cursor == COMMENTSYM)
686 skip_comment(&cursor);
688 else if(*cursor == ENUMLISTOPENSYM)
690 skip_enumlist(&cursor);
692 else if(*cursor == TAGSYM)
694 cursor++;
695 if(*cursor == '\0' || *cursor == '\n')
696 break;
697 cursor++;
699 else if(*cursor == ENUMLISTSEPERATESYM)
701 children++;
702 cursor++;
704 else
706 cursor++;
709 cursor = bookmark;
711 /* Parsing the children */
712 element->children_count = children + 1; /* Make sure to include the tag */
713 element->children = skin_alloc_children(children + 1);
714 element->children[0] = tag;
716 for(i = 1; i < children + 1; i++)
718 element->children[i] = skin_parse_code_as_arg(&cursor);
719 skip_whitespace(&cursor);
721 if(i < children && *cursor != ENUMLISTSEPERATESYM)
723 skin_error(SEPERATOR_EXPECTED);
724 return 0;
726 else if(i == children && *cursor != ENUMLISTCLOSESYM)
728 skin_error(CLOSE_EXPECTED);
729 return 0;
731 else
733 cursor++;
737 *document = cursor;
739 return 1;
742 int skin_parse_comment(struct skin_element* element, char** document)
744 char* cursor = *document;
746 int length;
748 * Finding the index of the ending newline or null-terminator
749 * The length of the string of interest doesn't include the leading #, the
750 * length we need to reserve is the same as the index of the last character
752 for(length = 0; cursor[length] != '\n' && cursor[length] != '\0'; length++);
754 element->type = COMMENT;
755 element->line = skin_line;
756 element->text = skin_alloc_string(length);
757 /* We copy from one char past cursor to leave out the # */
758 memcpy((void*)(element->text), (void*)(cursor + 1),
759 sizeof(char) * (length-1));
760 element->text[length - 1] = '\0';
762 if(cursor[length] == '\n')
763 skin_line++;
765 *document += (length); /* Move cursor up past # and all text */
766 if(**document == '\n')
767 (*document)++;
769 return 1;
772 struct skin_element* skin_parse_code_as_arg(char** document)
775 int sublines = 0;
776 char* cursor = *document;
778 /* Checking for sublines */
779 while(*cursor != '\n' && *cursor != '\0')
781 if(*cursor == MULTILINESYM)
783 sublines = 1;
784 break;
786 else if(*cursor == TAGSYM)
788 /* A ';' directly after a '%' doesn't count */
789 cursor ++;
791 if(*cursor == '\0')
792 break;
794 cursor++;
796 else
798 /* Advancing the cursor as normal */
799 cursor++;
803 if(sublines)
804 return skin_parse_sublines_optional(document, 1);
805 else
806 return skin_parse_line_optional(document, 1);
810 /* Memory management */
811 struct skin_element* skin_alloc_element()
814 #if 0
816 char* retval = &skin_parse_tree[skin_current_block * 4];
818 int delta = sizeof(struct skin_element) / (sizeof(char) * 4);
820 /* If one block is partially filled, make sure to advance to the
821 * next one for the next allocation
823 if(sizeof(struct skin_element) % (sizeof(char) * 4) != 0)
824 delta++;
826 skin_current_block += delta;
828 return (struct skin_element*)retval;
830 #endif
832 struct skin_element* retval = (struct skin_element*)
833 malloc(sizeof(struct skin_element));
834 retval->next = NULL;
835 retval->params_count = 0;
836 retval->children_count = 0;
838 return retval;
842 struct skin_tag_parameter* skin_alloc_params(int count)
844 #if 0
846 char* retval = &skin_parse_tree[skin_current_block * 4];
848 int delta = sizeof(struct skin_tag_parameter) / (sizeof(char) * 4);
849 delta *= count;
851 /* Correcting uneven alignment */
852 if(count * sizeof(struct skin_tag_parameter) % (sizeof(char) * 4) != 0)
853 delta++;
855 skin_current_block += delta;
857 return (struct skin_tag_parameter*) retval;
859 #endif
861 return (struct skin_tag_parameter*)malloc(sizeof(struct skin_tag_parameter)
862 * count);
866 char* skin_alloc_string(int length)
869 #if 0
870 char* retval = &skin_parse_tree[skin_current_block * 4];
872 /* Checking alignment */
873 length++; /* Accounting for the null terminator */
874 int delta = length / 4;
875 if(length % 4 != 0)
876 delta++;
878 skin_current_block += delta;
880 if(skin_current_block >= SKIN_MAX_MEMORY / 4)
881 skin_error(MEMORY_LIMIT_EXCEEDED);
883 return retval;
885 #endif
887 return (char*)malloc(sizeof(char) * (length + 1));
891 struct skin_element** skin_alloc_children(int count)
893 return (struct skin_element**) malloc(sizeof(struct skin_element*) * count);
896 void skin_free_tree(struct skin_element* root)
898 int i;
900 /* First make the recursive call */
901 if(!root)
902 return;
903 skin_free_tree(root->next);
905 /* Free any text */
906 if(root->type == TEXT)
907 free(root->text);
909 /* Then recursively free any children, before freeing their pointers */
910 for(i = 0; i < root->children_count; i++)
911 skin_free_tree(root->children[i]);
912 if(root->children_count > 0)
913 free(root->children);
915 /* Free any parameters, making sure to deallocate strings */
916 for(i = 0; i < root->params_count; i++)
917 if(root->params[i].type == STRING)
918 free(root->params[i].data.text);
919 if(root->params_count > 0)
920 free(root->params);
922 /* Finally, delete root's memory */
923 free(root);