Theme Editor: Fixed bugs in code generation and viewport parsing
[kugel-rb.git] / utils / themeeditor / skin_parser.c
blob347d675b55febf753abb15623d80889ed7529253
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 while(*cursor != '\0')
70 if(!root)
71 to_write = &root;
72 else
73 to_write = &(last->next);
76 *to_write = skin_parse_viewport(&cursor);
77 last = *to_write;
78 if(!last)
79 return NULL;
81 /* Making sure last is at the end */
82 while(last->next)
83 last = last->next;
87 return root;
91 struct skin_element* skin_parse_viewport(char** document)
94 struct skin_element* root = NULL;
95 struct skin_element* last = NULL;
96 struct skin_element* retval = NULL;
98 retval = skin_alloc_element();
99 retval->type = VIEWPORT;
100 retval->children_count = 1;
101 retval->line = skin_line;
103 struct skin_element** to_write = 0;
105 char* cursor = *document; /* Keeps track of location in the document */
106 char* bookmark; /* Used when we need to look ahead */
108 int sublines = 0; /* Flag for parsing sublines */
110 /* Parsing out the viewport tag if there is one */
111 if(check_viewport(cursor))
113 retval->children_count = 2;
114 retval->children = skin_alloc_children(2);
115 retval->children[0] = skin_alloc_element();
116 skin_parse_tag(retval->children[0], &cursor);
118 else
120 retval->children_count = 1;
121 retval->children = skin_alloc_children(1);
125 while(*cursor != '\0' && !(check_viewport(cursor) && cursor != *document))
128 /* First, we check to see if this line will contain sublines */
129 bookmark = cursor;
130 sublines = 0;
131 while(*cursor != '\n' && *cursor != '\0'
132 && !(check_viewport(cursor) && cursor != *document))
134 if(*cursor == MULTILINESYM)
136 sublines = 1;
137 break;
139 else if(*cursor == TAGSYM)
141 /* A ';' directly after a '%' doesn't count */
142 cursor ++;
144 if(*cursor == '\0')
145 break;
147 cursor++;
149 else if(*cursor == COMMENTSYM)
151 skip_comment(&cursor);
153 else
155 /* Advancing the cursor as normal */
156 cursor++;
159 cursor = bookmark;
161 if(!root)
162 to_write = &root;
163 else
164 to_write = &(last->next);
166 if(sublines)
168 *to_write = skin_parse_sublines(&cursor);
169 last = *to_write;
170 if(!last)
171 return NULL;
173 else
176 *to_write = skin_parse_line(&cursor);
177 last = *to_write;
178 if(!last)
179 return NULL;
183 /* Making sure last is at the end */
184 while(last->next)
185 last = last->next;
187 if(*cursor == '\n')
188 cursor++;
192 *document = cursor;
194 retval->children[retval->children_count - 1] = root;
195 return retval;
199 /* Auxiliary Parsing Functions */
201 struct skin_element* skin_parse_line(char**document)
204 return skin_parse_line_optional(document, 0);
210 * If conditional is set to true, then this will break upon encountering
211 * SEPERATESYM. This should only be used when parsing a line inside a
212 * conditional, otherwise just use the wrapper function skin_parse_line()
214 struct skin_element* skin_parse_line_optional(char** document, int conditional)
216 char* cursor = *document;
218 struct skin_element* root = NULL;
219 struct skin_element* current = NULL;
220 struct skin_element* retval = NULL;
222 /* A wrapper for the line */
223 retval = skin_alloc_element();
224 retval->type = LINE;
225 retval->line = skin_line;
226 retval->children_count = 1;
227 retval->children = skin_alloc_children(1);
229 while(*cursor != '\n' && *cursor != '\0' && *cursor != MULTILINESYM
230 && !((*cursor == ARGLISTSEPERATESYM
231 || *cursor == ARGLISTCLOSESYM
232 || *cursor == ENUMLISTSEPERATESYM
233 || *cursor == ENUMLISTCLOSESYM)
234 && conditional)
235 && !(check_viewport(cursor) && cursor != *document))
237 /* Allocating memory if necessary */
238 if(root)
240 current->next = skin_alloc_element();
241 current = current->next;
243 else
245 current = skin_alloc_element();
246 root = current;
249 /* Parsing the current element */
250 if(*cursor == TAGSYM && cursor[1] == CONDITIONSYM)
252 if(!skin_parse_conditional(current, &cursor))
253 return NULL;
255 else if(*cursor == TAGSYM && !find_escape_character(cursor[1]))
257 if(!skin_parse_tag(current, &cursor))
258 return NULL;
260 else if(*cursor == COMMENTSYM)
262 if(!skin_parse_comment(current, &cursor))
263 return NULL;
265 else
267 if(!skin_parse_text(current, &cursor, conditional))
268 return NULL;
272 /* Moving up the calling function's pointer */
273 *document = cursor;
275 retval->children[0] = root;
276 return retval;
279 struct skin_element* skin_parse_sublines(char** document)
281 return skin_parse_sublines_optional(document, 0);
284 struct skin_element* skin_parse_sublines_optional(char** document,
285 int conditional)
287 struct skin_element* retval;
288 char* cursor = *document;
289 int sublines = 1;
290 int i;
292 retval = skin_alloc_element();
293 retval->type = SUBLINES;
294 retval->next = NULL;
295 retval->line = skin_line;
297 /* First we count the sublines */
298 while(*cursor != '\0' && *cursor != '\n'
299 && !((*cursor == ARGLISTSEPERATESYM
300 || *cursor == ARGLISTCLOSESYM
301 || *cursor == ENUMLISTSEPERATESYM
302 || *cursor == ENUMLISTCLOSESYM)
303 && conditional)
304 && !(check_viewport(cursor) && cursor != *document))
306 if(*cursor == COMMENTSYM)
307 skip_comment(&cursor);
309 /* Accounting for escaped subline symbols */
310 if(*cursor == TAGSYM)
312 cursor++;
313 if(*cursor == '\0' || *cursor == '\n')
314 break;
317 if(*cursor == MULTILINESYM)
319 sublines++;
322 cursor++;
325 /* ...and then we parse them */
326 retval->children_count = sublines;
327 retval->children = skin_alloc_children(sublines);
329 cursor = *document;
330 for(i = 0; i < sublines; i++)
332 retval->children[i] = skin_parse_line_optional(&cursor, conditional);
333 skip_whitespace(&cursor);
335 if(*cursor != MULTILINESYM && i != sublines - 1)
337 skin_error(MULTILINE_EXPECTED);
338 return NULL;
340 else if(i != sublines - 1)
342 cursor++;
346 *document = cursor;
348 return retval;
351 int skin_parse_tag(struct skin_element* element, char** document)
354 char* cursor = *document + 1;
355 char* bookmark;
357 char tag_name[3];
358 char* tag_args;
359 struct tag_info *tag;
361 int num_args = 1;
362 int i;
363 int star = 0; /* Flag for the all-or-none option */
364 int req_args; /* To mark when we enter optional arguments */
366 int optional = 0;
368 /* Checking the tag name */
369 tag_name[0] = cursor[0];
370 tag_name[1] = cursor[1];
371 tag_name[2] = '\0';
373 /* First we check the two characters after the '%', then a single char */
374 tag = find_tag(tag_name);
376 if(!tag)
378 tag_name[1] = '\0';
379 tag = find_tag(tag_name);
380 cursor++;
382 else
384 cursor += 2;
387 if(!tag)
389 skin_error(ILLEGAL_TAG);
390 return 0;
393 /* Copying basic tag info */
394 element->type = TAG;
395 element->tag = tag;
396 tag_args = tag->params;
397 element->line = skin_line;
399 /* Checking for the * flag */
400 if(tag_args[0] == '*')
402 star = 1;
403 tag_args++;
406 /* If this tag has no arguments, we can bail out now */
407 if(strlen(tag_args) == 0
408 || (tag_args[0] == '|' && *cursor != ARGLISTOPENSYM)
409 || (star && *cursor != ARGLISTOPENSYM))
411 *document = cursor;
412 return 1;
415 /* Checking the number of arguments and allocating args */
416 if(*cursor != ARGLISTOPENSYM && tag_args[0] != '|')
418 skin_error(ARGLIST_EXPECTED);
419 return 0;
421 else
423 cursor++;
426 for(bookmark = cursor; *cursor != '\n' && *cursor != '\0' &&
427 *cursor != ARGLISTCLOSESYM; cursor++)
429 /* Skipping over escaped characters */
430 if(*cursor == TAGSYM)
432 cursor++;
433 if(*cursor == '\0')
434 break;
437 /* Skipping comments */
438 if(*cursor == COMMENTSYM)
439 skip_comment(&cursor);
441 /* Commas inside of contained lists don't count */
442 if(*cursor == ARGLISTOPENSYM)
443 while(*cursor != ARGLISTCLOSESYM && *cursor != '\0')
444 cursor++;
446 if(*cursor == ARGLISTSEPERATESYM)
447 num_args++;
450 cursor = bookmark; /* Restoring the cursor */
451 element->params_count = num_args;
452 element->params = skin_alloc_params(num_args);
454 /* Now we have to actually parse each argument */
455 for(i = 0; i < num_args; i++)
457 /* Making sure we haven't run out of arguments */
458 if(*tag_args == '\0')
460 skin_error(TOO_MANY_ARGS);
461 return 0;
464 /* Checking for the optional bar */
465 if(*tag_args == '|')
467 optional = 1;
468 req_args = i;
469 tag_args++;
472 /* Scanning the arguments */
473 skip_whitespace(&cursor);
476 /* Checking for comments */
477 if(*cursor == COMMENTSYM)
478 skip_comment(&cursor);
480 /* Storing the type code */
481 element->params[i].type_code = *tag_args;
483 /* Checking a nullable argument for null */
484 if(*cursor == DEFAULTSYM)
486 if(islower(*tag_args))
488 element->params[i].type = DEFAULT;
489 cursor++;
491 else
493 skin_error(DEFAULT_NOT_ALLOWED);
494 return 0;
497 else if(tolower(*tag_args) == 'i')
499 /* Scanning an int argument */
500 if(!isdigit(*cursor))
502 skin_error(INT_EXPECTED);
503 return 0;
506 element->params[i].type = NUMERIC;
507 element->params[i].data.numeric = scan_int(&cursor);
509 else if(tolower(*tag_args) == 's' || tolower(*tag_args) == 'f')
511 /* Scanning a string argument */
512 element->params[i].type = STRING;
513 element->params[i].data.text = scan_string(&cursor);
516 else if(tolower(*tag_args) == 'c')
518 /* Recursively parsing a code argument */
519 element->params[i].type = CODE;
520 element->params[i].data.code = skin_parse_code_as_arg(&cursor);
521 if(!element->params[i].data.code)
522 return 0;
525 skip_whitespace(&cursor);
527 if(*cursor != ARGLISTSEPERATESYM && i < num_args - 1)
529 skin_error(SEPERATOR_EXPECTED);
530 return 0;
532 else if(*cursor != ARGLISTCLOSESYM && i == num_args - 1)
534 skin_error(CLOSE_EXPECTED);
535 return 0;
537 else
539 cursor++;
542 tag_args++;
544 /* Checking for the optional bar */
545 if(*tag_args == '|')
547 optional = 1;
548 req_args = i + 1;
549 tag_args++;
554 /* Checking for a premature end */
555 if(*tag_args != '\0' && !optional)
557 skin_error(INSUFFICIENT_ARGS);
558 return 0;
561 *document = cursor;
563 return 1;
567 * If the conditional flag is set true, then parsing text will stop at an
568 * ARGLISTSEPERATESYM. Only set that flag when parsing within a conditional
570 int skin_parse_text(struct skin_element* element, char** document,
571 int conditional)
573 char* cursor = *document;
575 int length = 0;
577 int dest;
579 /* First figure out how much text we're copying */
580 while(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
581 && *cursor != COMMENTSYM
582 && !((*cursor == ARGLISTSEPERATESYM
583 || *cursor == ARGLISTCLOSESYM
584 || *cursor == ENUMLISTSEPERATESYM
585 || *cursor == ENUMLISTCLOSESYM)
586 && conditional))
588 /* Dealing with possibility of escaped characters */
589 if(*cursor == TAGSYM)
591 if(find_escape_character(cursor[1]))
592 cursor++;
593 else
594 break;
597 length++;
598 cursor++;
601 cursor = *document;
603 /* Copying the text into the element struct */
604 element->type = TEXT;
605 element->line = skin_line;
606 element->next = NULL;
607 element->text = skin_alloc_string(length);
609 for(dest = 0; dest < length; dest++)
611 /* Advancing cursor if we've encountered an escaped character */
612 if(*cursor == TAGSYM)
613 cursor++;
615 element->text[dest] = *cursor;
616 cursor++;
618 element->text[length] = '\0';
620 *document = cursor;
622 return 1;
625 int skin_parse_conditional(struct skin_element* element, char** document)
628 char* cursor = *document + 1; /* Starting past the "%" */
629 char* bookmark;
630 struct skin_element* tag = skin_alloc_element(); /* The tag to evaluate */
631 int children = 1;
632 int i;
634 element->type = CONDITIONAL;
635 element->line = skin_line;
637 /* Parsing the tag first */
638 if(!skin_parse_tag(tag, &cursor))
639 return 0;
641 /* Counting the children */
642 if(*(cursor++) != ENUMLISTOPENSYM)
644 skin_error(ARGLIST_EXPECTED);
645 return 0;
647 bookmark = cursor;
648 while(*cursor != ENUMLISTCLOSESYM && *cursor != '\n' && *cursor != '\0')
650 if(*cursor == COMMENTSYM)
652 skip_comment(&cursor);
653 continue;
656 if(*cursor == TAGSYM)
658 cursor++;
659 if(*cursor == '\0' || *cursor == '\n')
660 break;
661 cursor++;
662 continue;
665 if(*cursor == ENUMLISTSEPERATESYM)
666 children++;
668 cursor++;
670 cursor = bookmark;
672 /* Parsing the children */
673 element->children_count = children + 1; /* Make sure to include the tag */
674 element->children = skin_alloc_children(children + 1);
675 element->children[0] = tag;
677 for(i = 1; i < children + 1; i++)
679 element->children[i] = skin_parse_code_as_arg(&cursor);
680 skip_whitespace(&cursor);
682 if(i < children && *cursor != ENUMLISTSEPERATESYM)
684 skin_error(SEPERATOR_EXPECTED);
685 return 0;
687 else if(i == children && *cursor != ENUMLISTCLOSESYM)
689 skin_error(CLOSE_EXPECTED);
690 return 0;
692 else
694 cursor++;
698 *document = cursor;
700 return 1;
703 int skin_parse_comment(struct skin_element* element, char** document)
705 char* cursor = *document;
707 int length;
709 * Finding the index of the ending newline or null-terminator
710 * The length of the string of interest doesn't include the leading #, the
711 * length we need to reserve is the same as the index of the last character
713 for(length = 0; cursor[length] != '\n' && cursor[length] != '\0'; length++);
715 element->type = COMMENT;
716 element->line = skin_line;
717 element->text = skin_alloc_string(length);
718 /* We copy from one char past cursor to leave out the # */
719 memcpy((void*)(element->text), (void*)(cursor + 1),
720 sizeof(char) * (length-1));
721 element->text[length - 1] = '\0';
723 if(cursor[length] == '\n')
724 skin_line++;
726 *document += (length); /* Move cursor up past # and all text */
727 if(**document == '\n')
728 (*document)++;
730 return 1;
733 struct skin_element* skin_parse_code_as_arg(char** document)
736 int sublines = 0;
737 char* cursor = *document;
739 /* Checking for sublines */
740 while(*cursor != '\n' && *cursor != '\0')
742 if(*cursor == MULTILINESYM)
744 sublines = 1;
745 break;
747 else if(*cursor == TAGSYM)
749 /* A ';' directly after a '%' doesn't count */
750 cursor ++;
752 if(*cursor == '\0')
753 break;
755 cursor++;
757 else
759 /* Advancing the cursor as normal */
760 cursor++;
764 if(sublines)
765 return skin_parse_sublines_optional(document, 1);
766 else
767 return skin_parse_line_optional(document, 1);
771 /* Memory management */
772 struct skin_element* skin_alloc_element()
775 #if 0
777 char* retval = &skin_parse_tree[skin_current_block * 4];
779 int delta = sizeof(struct skin_element) / (sizeof(char) * 4);
781 /* If one block is partially filled, make sure to advance to the
782 * next one for the next allocation
784 if(sizeof(struct skin_element) % (sizeof(char) * 4) != 0)
785 delta++;
787 skin_current_block += delta;
789 return (struct skin_element*)retval;
791 #endif
793 struct skin_element* retval = (struct skin_element*)
794 malloc(sizeof(struct skin_element));
795 retval->next = NULL;
796 retval->params_count = 0;
797 retval->children_count = 0;
799 return retval;
803 struct skin_tag_parameter* skin_alloc_params(int count)
805 #if 0
807 char* retval = &skin_parse_tree[skin_current_block * 4];
809 int delta = sizeof(struct skin_tag_parameter) / (sizeof(char) * 4);
810 delta *= count;
812 /* Correcting uneven alignment */
813 if(count * sizeof(struct skin_tag_parameter) % (sizeof(char) * 4) != 0)
814 delta++;
816 skin_current_block += delta;
818 return (struct skin_tag_parameter*) retval;
820 #endif
822 return (struct skin_tag_parameter*)malloc(sizeof(struct skin_tag_parameter)
823 * count);
827 char* skin_alloc_string(int length)
830 #if 0
831 char* retval = &skin_parse_tree[skin_current_block * 4];
833 /* Checking alignment */
834 length++; /* Accounting for the null terminator */
835 int delta = length / 4;
836 if(length % 4 != 0)
837 delta++;
839 skin_current_block += delta;
841 if(skin_current_block >= SKIN_MAX_MEMORY / 4)
842 skin_error(MEMORY_LIMIT_EXCEEDED);
844 return retval;
846 #endif
848 return (char*)malloc(sizeof(char) * (length + 1));
852 struct skin_element** skin_alloc_children(int count)
854 return (struct skin_element**) malloc(sizeof(struct skin_element*) * count);
857 void skin_free_tree(struct skin_element* root)
859 int i;
861 /* First make the recursive call */
862 if(!root)
863 return;
864 skin_free_tree(root->next);
866 /* Free any text */
867 if(root->type == TEXT)
868 free(root->text);
870 /* Then recursively free any children, before freeing their pointers */
871 for(i = 0; i < root->children_count; i++)
872 skin_free_tree(root->children[i]);
873 if(root->children_count > 0)
874 free(root->children);
876 /* Free any parameters, making sure to deallocate strings */
877 for(i = 0; i < root->params_count; i++)
878 if(root->params[i].type == STRING)
879 free(root->params[i].data.text);
880 if(root->params_count > 0)
881 free(root->params);
883 /* Finally, delete root's memory */
884 free(root);