Theme Editor: Changed default font to monospaced, changed organization for the applic...
[kugel-rb.git] / utils / themeeditor / skin_parser.c
blob58acafb56a7c1061105829bf6bb06e25c45febb7
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
162 /* Advancing the cursor as normal */
163 cursor++;
166 cursor = bookmark;
168 if(!root)
169 to_write = &root;
170 else
171 to_write = &(last->next);
173 if(sublines)
175 *to_write = skin_parse_sublines(&cursor);
176 last = *to_write;
177 if(!last)
178 return NULL;
180 else
183 *to_write = skin_parse_line(&cursor);
184 last = *to_write;
185 if(!last)
186 return NULL;
190 /* Making sure last is at the end */
191 while(last->next)
192 last = last->next;
194 if(*cursor == '\n')
196 cursor++;
197 skin_line++;
201 *document = cursor;
203 retval->children[retval->children_count - 1] = root;
204 return retval;
208 /* Auxiliary Parsing Functions */
210 struct skin_element* skin_parse_line(char**document)
213 return skin_parse_line_optional(document, 0);
219 * If conditional is set to true, then this will break upon encountering
220 * SEPERATESYM. This should only be used when parsing a line inside a
221 * conditional, otherwise just use the wrapper function skin_parse_line()
223 struct skin_element* skin_parse_line_optional(char** document, int conditional)
225 char* cursor = *document;
227 struct skin_element* root = NULL;
228 struct skin_element* current = NULL;
229 struct skin_element* retval = NULL;
231 /* A wrapper for the line */
232 retval = skin_alloc_element();
233 retval->type = LINE;
234 retval->line = skin_line;
235 retval->children_count = 1;
236 retval->children = skin_alloc_children(1);
238 while(*cursor != '\n' && *cursor != '\0' && *cursor != MULTILINESYM
239 && !((*cursor == ARGLISTSEPERATESYM
240 || *cursor == ARGLISTCLOSESYM
241 || *cursor == ENUMLISTSEPERATESYM
242 || *cursor == ENUMLISTCLOSESYM)
243 && conditional)
244 && !(check_viewport(cursor) && cursor != *document))
246 /* Allocating memory if necessary */
247 if(root)
249 current->next = skin_alloc_element();
250 current = current->next;
252 else
254 current = skin_alloc_element();
255 root = current;
258 /* Parsing the current element */
259 if(*cursor == TAGSYM && cursor[1] == CONDITIONSYM)
261 if(!skin_parse_conditional(current, &cursor))
262 return NULL;
264 else if(*cursor == TAGSYM && !find_escape_character(cursor[1]))
266 if(!skin_parse_tag(current, &cursor))
267 return NULL;
269 else if(*cursor == COMMENTSYM)
271 if(!skin_parse_comment(current, &cursor))
272 return NULL;
274 else
276 if(!skin_parse_text(current, &cursor, conditional))
277 return NULL;
281 /* Moving up the calling function's pointer */
282 *document = cursor;
284 retval->children[0] = root;
285 return retval;
288 struct skin_element* skin_parse_sublines(char** document)
290 return skin_parse_sublines_optional(document, 0);
293 struct skin_element* skin_parse_sublines_optional(char** document,
294 int conditional)
296 struct skin_element* retval;
297 char* cursor = *document;
298 int sublines = 1;
299 int i;
300 int nested = 0;
302 retval = skin_alloc_element();
303 retval->type = SUBLINES;
304 retval->next = NULL;
305 retval->line = skin_line;
307 /* First we count the sublines */
308 while(*cursor != '\0' && *cursor != '\n'
309 && !((*cursor == ARGLISTSEPERATESYM
310 || *cursor == ARGLISTCLOSESYM
311 || *cursor == ENUMLISTSEPERATESYM
312 || *cursor == ENUMLISTCLOSESYM)
313 && conditional)
314 && !(check_viewport(cursor) && cursor != *document))
316 if(*cursor == COMMENTSYM)
318 skip_comment(&cursor);
319 continue;
322 if(*cursor == ENUMLISTOPENSYM && conditional)
324 nested++;
325 cursor++;
326 while(nested)
328 if(*cursor == ENUMLISTOPENSYM)
329 nested++;
330 if(*cursor == ENUMLISTCLOSESYM)
331 nested--;
332 cursor++;
335 /* Accounting for escaped subline symbols */
336 if(*cursor == TAGSYM)
338 cursor++;
339 if(*cursor == '\0' || *cursor == '\n')
340 break;
343 if(*cursor == MULTILINESYM)
345 sublines++;
348 cursor++;
351 /* ...and then we parse them */
352 retval->children_count = sublines;
353 retval->children = skin_alloc_children(sublines);
355 cursor = *document;
356 for(i = 0; i < sublines; i++)
358 retval->children[i] = skin_parse_line_optional(&cursor, conditional);
359 skip_whitespace(&cursor);
361 if(*cursor != MULTILINESYM && i != sublines - 1)
363 skin_error(MULTILINE_EXPECTED);
364 return NULL;
366 else if(i != sublines - 1)
368 cursor++;
372 *document = cursor;
374 return retval;
377 int skin_parse_tag(struct skin_element* element, char** document)
380 char* cursor = *document + 1;
381 char* bookmark;
383 char tag_name[3];
384 char* tag_args;
385 struct tag_info *tag;
387 int num_args = 1;
388 int i;
389 int star = 0; /* Flag for the all-or-none option */
390 int req_args; /* To mark when we enter optional arguments */
392 int optional = 0;
394 /* Checking the tag name */
395 tag_name[0] = cursor[0];
396 tag_name[1] = cursor[1];
397 tag_name[2] = '\0';
399 /* First we check the two characters after the '%', then a single char */
400 tag = find_tag(tag_name);
402 if(!tag)
404 tag_name[1] = '\0';
405 tag = find_tag(tag_name);
406 cursor++;
408 else
410 cursor += 2;
413 if(!tag)
415 skin_error(ILLEGAL_TAG);
416 return 0;
419 /* Copying basic tag info */
420 element->type = TAG;
421 element->tag = tag;
422 tag_args = tag->params;
423 element->line = skin_line;
425 /* Checking for the * flag */
426 if(tag_args[0] == '*')
428 star = 1;
429 tag_args++;
432 /* If this tag has no arguments, we can bail out now */
433 if(strlen(tag_args) == 0
434 || (tag_args[0] == '|' && *cursor != ARGLISTOPENSYM)
435 || (star && *cursor != ARGLISTOPENSYM))
437 *document = cursor;
438 return 1;
441 /* Checking the number of arguments and allocating args */
442 if(*cursor != ARGLISTOPENSYM && tag_args[0] != '|')
444 skin_error(ARGLIST_EXPECTED);
445 return 0;
447 else
449 cursor++;
452 for(bookmark = cursor; *cursor != '\n' && *cursor != '\0' &&
453 *cursor != ARGLISTCLOSESYM; cursor++)
455 /* Skipping over escaped characters */
456 if(*cursor == TAGSYM)
458 cursor++;
459 if(*cursor == '\0')
460 break;
463 /* Skipping comments */
464 if(*cursor == COMMENTSYM)
465 skip_comment(&cursor);
467 /* Commas inside of contained lists don't count */
468 if(*cursor == ARGLISTOPENSYM)
469 while(*cursor != ARGLISTCLOSESYM && *cursor != '\0')
470 cursor++;
472 if(*cursor == ARGLISTSEPERATESYM)
473 num_args++;
476 cursor = bookmark; /* Restoring the cursor */
477 element->params_count = num_args;
478 element->params = skin_alloc_params(num_args);
480 /* Now we have to actually parse each argument */
481 for(i = 0; i < num_args; i++)
483 /* Making sure we haven't run out of arguments */
484 if(*tag_args == '\0')
486 skin_error(TOO_MANY_ARGS);
487 return 0;
490 /* Checking for the optional bar */
491 if(*tag_args == '|')
493 optional = 1;
494 req_args = i;
495 tag_args++;
498 /* Scanning the arguments */
499 skip_whitespace(&cursor);
502 /* Checking for comments */
503 if(*cursor == COMMENTSYM)
504 skip_comment(&cursor);
506 /* Storing the type code */
507 element->params[i].type_code = *tag_args;
509 /* Checking a nullable argument for null */
510 if(*cursor == DEFAULTSYM && !isdigit(cursor[1]))
512 if(islower(*tag_args))
514 element->params[i].type = DEFAULT;
515 cursor++;
517 else
519 skin_error(DEFAULT_NOT_ALLOWED);
520 return 0;
523 else if(tolower(*tag_args) == 'i')
525 /* Scanning an int argument */
526 if(!isdigit(*cursor) && *cursor != '-')
528 skin_error(INT_EXPECTED);
529 return 0;
532 element->params[i].type = NUMERIC;
533 element->params[i].data.numeric = scan_int(&cursor);
535 else if(tolower(*tag_args) == 's' || tolower(*tag_args) == 'f')
537 /* Scanning a string argument */
538 element->params[i].type = STRING;
539 element->params[i].data.text = scan_string(&cursor);
542 else if(tolower(*tag_args) == 'c')
544 /* Recursively parsing a code argument */
545 element->params[i].type = CODE;
546 element->params[i].data.code = skin_parse_code_as_arg(&cursor);
547 if(!element->params[i].data.code)
548 return 0;
551 skip_whitespace(&cursor);
553 if(*cursor != ARGLISTSEPERATESYM && i < num_args - 1)
555 skin_error(SEPERATOR_EXPECTED);
556 return 0;
558 else if(*cursor != ARGLISTCLOSESYM && i == num_args - 1)
560 skin_error(CLOSE_EXPECTED);
561 return 0;
563 else
565 cursor++;
568 tag_args++;
570 /* Checking for the optional bar */
571 if(*tag_args == '|')
573 optional = 1;
574 req_args = i + 1;
575 tag_args++;
580 /* Checking for a premature end */
581 if(*tag_args != '\0' && !optional)
583 skin_error(INSUFFICIENT_ARGS);
584 return 0;
587 *document = cursor;
589 return 1;
593 * If the conditional flag is set true, then parsing text will stop at an
594 * ARGLISTSEPERATESYM. Only set that flag when parsing within a conditional
596 int skin_parse_text(struct skin_element* element, char** document,
597 int conditional)
599 char* cursor = *document;
601 int length = 0;
603 int dest;
605 /* First figure out how much text we're copying */
606 while(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
607 && *cursor != COMMENTSYM
608 && !((*cursor == ARGLISTSEPERATESYM
609 || *cursor == ARGLISTCLOSESYM
610 || *cursor == ENUMLISTSEPERATESYM
611 || *cursor == ENUMLISTCLOSESYM)
612 && conditional))
614 /* Dealing with possibility of escaped characters */
615 if(*cursor == TAGSYM)
617 if(find_escape_character(cursor[1]))
618 cursor++;
619 else
620 break;
623 length++;
624 cursor++;
627 cursor = *document;
629 /* Copying the text into the element struct */
630 element->type = TEXT;
631 element->line = skin_line;
632 element->next = NULL;
633 element->text = skin_alloc_string(length);
635 for(dest = 0; dest < length; dest++)
637 /* Advancing cursor if we've encountered an escaped character */
638 if(*cursor == TAGSYM)
639 cursor++;
641 element->text[dest] = *cursor;
642 cursor++;
644 element->text[length] = '\0';
646 *document = cursor;
648 return 1;
651 int skin_parse_conditional(struct skin_element* element, char** document)
654 char* cursor = *document + 1; /* Starting past the "%" */
655 char* bookmark;
656 struct skin_element* tag = skin_alloc_element(); /* The tag to evaluate */
657 int children = 1;
658 int i;
659 int nested = 0;
661 element->type = CONDITIONAL;
662 element->line = skin_line;
664 /* Parsing the tag first */
665 if(!skin_parse_tag(tag, &cursor))
666 return 0;
668 /* Counting the children */
669 if(*(cursor++) != ENUMLISTOPENSYM)
671 skin_error(ARGLIST_EXPECTED);
672 return 0;
674 bookmark = cursor;
675 while(*cursor != ENUMLISTCLOSESYM && *cursor != '\n' && *cursor != '\0')
677 if(*cursor == COMMENTSYM)
679 skip_comment(&cursor);
680 continue;
683 if(*cursor == ENUMLISTOPENSYM)
685 nested++;
686 cursor++;
687 while(nested)
689 if(*cursor == ENUMLISTOPENSYM)
691 nested++;
693 else if(*cursor == ENUMLISTCLOSESYM)
695 nested--;
696 if(nested == 0)
697 break;
699 cursor++;
703 if(*cursor == TAGSYM)
705 cursor++;
706 if(*cursor == '\0' || *cursor == '\n')
707 break;
708 cursor++;
709 continue;
712 if(*cursor == ENUMLISTSEPERATESYM)
713 children++;
715 cursor++;
717 cursor = bookmark;
719 /* Parsing the children */
720 element->children_count = children + 1; /* Make sure to include the tag */
721 element->children = skin_alloc_children(children + 1);
722 element->children[0] = tag;
724 for(i = 1; i < children + 1; i++)
726 element->children[i] = skin_parse_code_as_arg(&cursor);
727 skip_whitespace(&cursor);
729 if(i < children && *cursor != ENUMLISTSEPERATESYM)
731 skin_error(SEPERATOR_EXPECTED);
732 return 0;
734 else if(i == children && *cursor != ENUMLISTCLOSESYM)
736 skin_error(CLOSE_EXPECTED);
737 return 0;
739 else
741 cursor++;
745 *document = cursor;
747 return 1;
750 int skin_parse_comment(struct skin_element* element, char** document)
752 char* cursor = *document;
754 int length;
756 * Finding the index of the ending newline or null-terminator
757 * The length of the string of interest doesn't include the leading #, the
758 * length we need to reserve is the same as the index of the last character
760 for(length = 0; cursor[length] != '\n' && cursor[length] != '\0'; length++);
762 element->type = COMMENT;
763 element->line = skin_line;
764 element->text = skin_alloc_string(length);
765 /* We copy from one char past cursor to leave out the # */
766 memcpy((void*)(element->text), (void*)(cursor + 1),
767 sizeof(char) * (length-1));
768 element->text[length - 1] = '\0';
770 if(cursor[length] == '\n')
771 skin_line++;
773 *document += (length); /* Move cursor up past # and all text */
774 if(**document == '\n')
775 (*document)++;
777 return 1;
780 struct skin_element* skin_parse_code_as_arg(char** document)
783 int sublines = 0;
784 char* cursor = *document;
786 /* Checking for sublines */
787 while(*cursor != '\n' && *cursor != '\0')
789 if(*cursor == MULTILINESYM)
791 sublines = 1;
792 break;
794 else if(*cursor == TAGSYM)
796 /* A ';' directly after a '%' doesn't count */
797 cursor ++;
799 if(*cursor == '\0')
800 break;
802 cursor++;
804 else
806 /* Advancing the cursor as normal */
807 cursor++;
811 if(sublines)
812 return skin_parse_sublines_optional(document, 1);
813 else
814 return skin_parse_line_optional(document, 1);
818 /* Memory management */
819 struct skin_element* skin_alloc_element()
822 #if 0
824 char* retval = &skin_parse_tree[skin_current_block * 4];
826 int delta = sizeof(struct skin_element) / (sizeof(char) * 4);
828 /* If one block is partially filled, make sure to advance to the
829 * next one for the next allocation
831 if(sizeof(struct skin_element) % (sizeof(char) * 4) != 0)
832 delta++;
834 skin_current_block += delta;
836 return (struct skin_element*)retval;
838 #endif
840 struct skin_element* retval = (struct skin_element*)
841 malloc(sizeof(struct skin_element));
842 retval->next = NULL;
843 retval->params_count = 0;
844 retval->children_count = 0;
846 return retval;
850 struct skin_tag_parameter* skin_alloc_params(int count)
852 #if 0
854 char* retval = &skin_parse_tree[skin_current_block * 4];
856 int delta = sizeof(struct skin_tag_parameter) / (sizeof(char) * 4);
857 delta *= count;
859 /* Correcting uneven alignment */
860 if(count * sizeof(struct skin_tag_parameter) % (sizeof(char) * 4) != 0)
861 delta++;
863 skin_current_block += delta;
865 return (struct skin_tag_parameter*) retval;
867 #endif
869 return (struct skin_tag_parameter*)malloc(sizeof(struct skin_tag_parameter)
870 * count);
874 char* skin_alloc_string(int length)
877 #if 0
878 char* retval = &skin_parse_tree[skin_current_block * 4];
880 /* Checking alignment */
881 length++; /* Accounting for the null terminator */
882 int delta = length / 4;
883 if(length % 4 != 0)
884 delta++;
886 skin_current_block += delta;
888 if(skin_current_block >= SKIN_MAX_MEMORY / 4)
889 skin_error(MEMORY_LIMIT_EXCEEDED);
891 return retval;
893 #endif
895 return (char*)malloc(sizeof(char) * (length + 1));
899 struct skin_element** skin_alloc_children(int count)
901 return (struct skin_element**) malloc(sizeof(struct skin_element*) * count);
904 void skin_free_tree(struct skin_element* root)
906 int i;
908 /* First make the recursive call */
909 if(!root)
910 return;
911 skin_free_tree(root->next);
913 /* Free any text */
914 if(root->type == TEXT)
915 free(root->text);
917 /* Then recursively free any children, before freeing their pointers */
918 for(i = 0; i < root->children_count; i++)
919 skin_free_tree(root->children[i]);
920 if(root->children_count > 0)
921 free(root->children);
923 /* Free any parameters, making sure to deallocate strings */
924 for(i = 0; i < root->params_count; i++)
925 if(root->params[i].type == STRING)
926 free(root->params[i].data.text);
927 if(root->params_count > 0)
928 free(root->params);
930 /* Finally, delete root's memory */
931 free(root);