1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
27 #include "skin_parser.h"
28 #include "skin_debug.h"
29 #include "tag_table.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 */
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
,
48 int skin_parse_tag(struct skin_element
* element
, char** document
);
49 int skin_parse_text(struct skin_element
* element
, char** document
,
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*/
69 while(*cursor
!= '\0')
75 to_write
= &(last
->next
);
78 *to_write
= skin_parse_viewport(&cursor
);
82 skin_free_tree(root
); /* Clearing any memory already used */
86 /* Making sure last is at the end */
96 struct skin_element
* skin_parse_viewport(char** document
)
99 struct skin_element
* root
= NULL
;
100 struct skin_element
* last
= NULL
;
101 struct skin_element
* retval
= NULL
;
103 retval
= skin_alloc_element();
104 retval
->type
= VIEWPORT
;
105 retval
->children_count
= 1;
106 retval
->line
= skin_line
;
108 struct skin_element
** to_write
= 0;
110 char* cursor
= *document
; /* Keeps track of location in the document */
111 char* bookmark
; /* Used when we need to look ahead */
113 int sublines
= 0; /* Flag for parsing sublines */
115 /* Parsing out the viewport tag if there is one */
116 if(check_viewport(cursor
))
118 retval
->children_count
= 2;
119 retval
->children
= skin_alloc_children(2);
120 retval
->children
[0] = skin_alloc_element();
121 skin_parse_tag(retval
->children
[0], &cursor
);
130 retval
->children_count
= 1;
131 retval
->children
= skin_alloc_children(1);
135 while(*cursor
!= '\0' && !(check_viewport(cursor
) && cursor
!= *document
))
138 /* First, we check to see if this line will contain sublines */
141 while(*cursor
!= '\n' && *cursor
!= '\0'
142 && !(check_viewport(cursor
) && cursor
!= *document
))
144 if(*cursor
== MULTILINESYM
)
149 else if(*cursor
== TAGSYM
)
151 /* A ';' directly after a '%' doesn't count */
159 else if(*cursor
== COMMENTSYM
)
161 skip_comment(&cursor
);
163 else if(*cursor
== ARGLISTOPENSYM
)
165 skip_arglist(&cursor
);
167 else if(*cursor
== ENUMLISTOPENSYM
)
169 skip_arglist(&cursor
);
173 /* Advancing the cursor as normal */
182 to_write
= &(last
->next
);
186 *to_write
= skin_parse_sublines(&cursor
);
194 *to_write
= skin_parse_line(&cursor
);
201 /* Making sure last is at the end */
214 retval
->children
[retval
->children_count
- 1] = root
;
219 /* Auxiliary Parsing Functions */
221 struct skin_element
* skin_parse_line(char**document
)
224 return skin_parse_line_optional(document
, 0);
230 * If conditional is set to true, then this will break upon encountering
231 * SEPERATESYM. This should only be used when parsing a line inside a
232 * conditional, otherwise just use the wrapper function skin_parse_line()
234 struct skin_element
* skin_parse_line_optional(char** document
, int conditional
)
236 char* cursor
= *document
;
238 struct skin_element
* root
= NULL
;
239 struct skin_element
* current
= NULL
;
240 struct skin_element
* retval
= NULL
;
242 /* A wrapper for the line */
243 retval
= skin_alloc_element();
245 retval
->line
= skin_line
;
246 retval
->children_count
= 1;
247 retval
->children
= skin_alloc_children(1);
249 while(*cursor
!= '\n' && *cursor
!= '\0' && *cursor
!= MULTILINESYM
250 && !((*cursor
== ARGLISTSEPERATESYM
251 || *cursor
== ARGLISTCLOSESYM
252 || *cursor
== ENUMLISTSEPERATESYM
253 || *cursor
== ENUMLISTCLOSESYM
)
255 && !(check_viewport(cursor
) && cursor
!= *document
))
257 /* Allocating memory if necessary */
260 current
->next
= skin_alloc_element();
261 current
= current
->next
;
265 current
= skin_alloc_element();
269 /* Parsing the current element */
270 if(*cursor
== TAGSYM
&& cursor
[1] == CONDITIONSYM
)
272 if(!skin_parse_conditional(current
, &cursor
))
275 else if(*cursor
== TAGSYM
&& !find_escape_character(cursor
[1]))
277 if(!skin_parse_tag(current
, &cursor
))
280 else if(*cursor
== COMMENTSYM
)
282 if(!skin_parse_comment(current
, &cursor
))
287 if(!skin_parse_text(current
, &cursor
, conditional
))
292 /* Moving up the calling function's pointer */
295 retval
->children
[0] = root
;
299 struct skin_element
* skin_parse_sublines(char** document
)
301 return skin_parse_sublines_optional(document
, 0);
304 struct skin_element
* skin_parse_sublines_optional(char** document
,
307 struct skin_element
* retval
;
308 char* cursor
= *document
;
312 retval
= skin_alloc_element();
313 retval
->type
= SUBLINES
;
315 retval
->line
= skin_line
;
317 /* First we count the sublines */
318 while(*cursor
!= '\0' && *cursor
!= '\n'
319 && !((*cursor
== ARGLISTSEPERATESYM
320 || *cursor
== ARGLISTCLOSESYM
321 || *cursor
== ENUMLISTSEPERATESYM
322 || *cursor
== ENUMLISTCLOSESYM
)
324 && !(check_viewport(cursor
) && cursor
!= *document
))
326 if(*cursor
== COMMENTSYM
)
328 skip_comment(&cursor
);
330 else if(*cursor
== ENUMLISTOPENSYM
)
332 skip_enumlist(&cursor
);
334 else if(*cursor
== ARGLISTOPENSYM
)
336 skip_arglist(&cursor
);
338 else if(*cursor
== TAGSYM
)
341 if(*cursor
== '\0' || *cursor
== '\n')
345 else if(*cursor
== MULTILINESYM
)
356 /* ...and then we parse them */
357 retval
->children_count
= sublines
;
358 retval
->children
= skin_alloc_children(sublines
);
361 for(i
= 0; i
< sublines
; i
++)
363 retval
->children
[i
] = skin_parse_line_optional(&cursor
, conditional
);
364 skip_whitespace(&cursor
);
366 if(*cursor
!= MULTILINESYM
&& i
!= sublines
- 1)
368 skin_error(MULTILINE_EXPECTED
);
371 else if(i
!= sublines
- 1)
382 int skin_parse_tag(struct skin_element
* element
, char** document
)
385 char* cursor
= *document
+ 1;
390 struct tag_info
*tag
;
394 int star
= 0; /* Flag for the all-or-none option */
395 int req_args
; /* To mark when we enter optional arguments */
399 /* Checking the tag name */
400 tag_name
[0] = cursor
[0];
401 tag_name
[1] = cursor
[1];
404 /* First we check the two characters after the '%', then a single char */
405 tag
= find_tag(tag_name
);
410 tag
= find_tag(tag_name
);
420 skin_error(ILLEGAL_TAG
);
424 /* Copying basic tag info */
427 tag_args
= tag
->params
;
428 element
->line
= skin_line
;
430 /* Checking for the * flag */
431 if(tag_args
[0] == '*')
437 /* If this tag has no arguments, we can bail out now */
438 if(strlen(tag_args
) == 0
439 || (tag_args
[0] == '|' && *cursor
!= ARGLISTOPENSYM
)
440 || (star
&& *cursor
!= ARGLISTOPENSYM
))
446 /* Checking the number of arguments and allocating args */
447 if(*cursor
!= ARGLISTOPENSYM
&& tag_args
[0] != '|')
449 skin_error(ARGLIST_EXPECTED
);
458 while(*cursor
!= '\n' && *cursor
!= '\0' && *cursor
!= ARGLISTCLOSESYM
)
460 /* Skipping over escaped characters */
461 if(*cursor
== TAGSYM
)
468 else if(*cursor
== COMMENTSYM
)
470 skip_comment(&cursor
);
472 else if(*cursor
== ARGLISTOPENSYM
)
474 skip_arglist(&cursor
);
476 else if(*cursor
== ARGLISTSEPERATESYM
)
487 cursor
= bookmark
; /* Restoring the cursor */
488 element
->params_count
= num_args
;
489 element
->params
= skin_alloc_params(num_args
);
491 /* Now we have to actually parse each argument */
492 for(i
= 0; i
< num_args
; i
++)
494 /* Making sure we haven't run out of arguments */
495 if(*tag_args
== '\0')
497 skin_error(TOO_MANY_ARGS
);
501 /* Checking for the optional bar */
509 /* Scanning the arguments */
510 skip_whitespace(&cursor
);
513 /* Checking for comments */
514 if(*cursor
== COMMENTSYM
)
515 skip_comment(&cursor
);
517 /* Storing the type code */
518 element
->params
[i
].type_code
= *tag_args
;
520 /* Checking a nullable argument for null */
521 if(*cursor
== DEFAULTSYM
&& !isdigit(cursor
[1]))
523 if(islower(*tag_args
))
525 element
->params
[i
].type
= DEFAULT
;
530 skin_error(DEFAULT_NOT_ALLOWED
);
534 else if(tolower(*tag_args
) == 'i')
536 /* Scanning an int argument */
537 if(!isdigit(*cursor
) && *cursor
!= '-')
539 skin_error(INT_EXPECTED
);
543 element
->params
[i
].type
= NUMERIC
;
544 element
->params
[i
].data
.numeric
= scan_int(&cursor
);
546 else if(tolower(*tag_args
) == 's' || tolower(*tag_args
) == 'f')
548 /* Scanning a string argument */
549 element
->params
[i
].type
= STRING
;
550 element
->params
[i
].data
.text
= scan_string(&cursor
);
553 else if(tolower(*tag_args
) == 'c')
555 /* Recursively parsing a code argument */
556 element
->params
[i
].type
= CODE
;
557 element
->params
[i
].data
.code
= skin_parse_code_as_arg(&cursor
);
558 if(!element
->params
[i
].data
.code
)
562 skip_whitespace(&cursor
);
564 if(*cursor
!= ARGLISTSEPERATESYM
&& i
< num_args
- 1)
566 skin_error(SEPERATOR_EXPECTED
);
569 else if(*cursor
!= ARGLISTCLOSESYM
&& i
== num_args
- 1)
571 skin_error(CLOSE_EXPECTED
);
581 /* Checking for the optional bar */
591 /* Checking for a premature end */
592 if(*tag_args
!= '\0' && !optional
)
594 skin_error(INSUFFICIENT_ARGS
);
604 * If the conditional flag is set true, then parsing text will stop at an
605 * ARGLISTSEPERATESYM. Only set that flag when parsing within a conditional
607 int skin_parse_text(struct skin_element
* element
, char** document
,
610 char* cursor
= *document
;
616 /* First figure out how much text we're copying */
617 while(*cursor
!= '\0' && *cursor
!= '\n' && *cursor
!= MULTILINESYM
618 && *cursor
!= COMMENTSYM
619 && !((*cursor
== ARGLISTSEPERATESYM
620 || *cursor
== ARGLISTCLOSESYM
621 || *cursor
== ENUMLISTSEPERATESYM
622 || *cursor
== ENUMLISTCLOSESYM
)
625 /* Dealing with possibility of escaped characters */
626 if(*cursor
== TAGSYM
)
628 if(find_escape_character(cursor
[1]))
640 /* Copying the text into the element struct */
641 element
->type
= TEXT
;
642 element
->line
= skin_line
;
643 element
->next
= NULL
;
644 element
->text
= skin_alloc_string(length
);
646 for(dest
= 0; dest
< length
; dest
++)
648 /* Advancing cursor if we've encountered an escaped character */
649 if(*cursor
== TAGSYM
)
652 element
->text
[dest
] = *cursor
;
655 element
->text
[length
] = '\0';
662 int skin_parse_conditional(struct skin_element
* element
, char** document
)
665 char* cursor
= *document
+ 1; /* Starting past the "%" */
667 struct skin_element
* tag
= skin_alloc_element(); /* The tag to evaluate */
671 element
->type
= CONDITIONAL
;
672 element
->line
= skin_line
;
674 /* Parsing the tag first */
675 if(!skin_parse_tag(tag
, &cursor
))
678 /* Counting the children */
679 if(*(cursor
++) != ENUMLISTOPENSYM
)
681 skin_error(ARGLIST_EXPECTED
);
685 while(*cursor
!= ENUMLISTCLOSESYM
&& *cursor
!= '\n' && *cursor
!= '\0')
687 if(*cursor
== COMMENTSYM
)
689 skip_comment(&cursor
);
691 else if(*cursor
== ENUMLISTOPENSYM
)
693 skip_enumlist(&cursor
);
695 else if(*cursor
== TAGSYM
)
698 if(*cursor
== '\0' || *cursor
== '\n')
702 else if(*cursor
== ENUMLISTSEPERATESYM
)
714 /* Parsing the children */
715 element
->children_count
= children
+ 1; /* Make sure to include the tag */
716 element
->children
= skin_alloc_children(children
+ 1);
717 element
->children
[0] = tag
;
719 for(i
= 1; i
< children
+ 1; i
++)
721 element
->children
[i
] = skin_parse_code_as_arg(&cursor
);
722 skip_whitespace(&cursor
);
724 if(i
< children
&& *cursor
!= ENUMLISTSEPERATESYM
)
726 skin_error(SEPERATOR_EXPECTED
);
729 else if(i
== children
&& *cursor
!= ENUMLISTCLOSESYM
)
731 skin_error(CLOSE_EXPECTED
);
745 int skin_parse_comment(struct skin_element
* element
, char** document
)
747 char* cursor
= *document
;
751 * Finding the index of the ending newline or null-terminator
752 * The length of the string of interest doesn't include the leading #, the
753 * length we need to reserve is the same as the index of the last character
755 for(length
= 0; cursor
[length
] != '\n' && cursor
[length
] != '\0'; length
++);
757 element
->type
= COMMENT
;
758 element
->line
= skin_line
;
759 element
->text
= skin_alloc_string(length
);
760 /* We copy from one char past cursor to leave out the # */
761 memcpy((void*)(element
->text
), (void*)(cursor
+ 1),
762 sizeof(char) * (length
-1));
763 element
->text
[length
- 1] = '\0';
765 if(cursor
[length
] == '\n')
768 *document
+= (length
); /* Move cursor up past # and all text */
769 if(**document
== '\n')
775 struct skin_element
* skin_parse_code_as_arg(char** document
)
779 char* cursor
= *document
;
781 /* Checking for sublines */
782 while(*cursor
!= '\n' && *cursor
!= '\0')
784 if(*cursor
== MULTILINESYM
)
789 else if(*cursor
== TAGSYM
)
791 /* A ';' directly after a '%' doesn't count */
801 /* Advancing the cursor as normal */
807 return skin_parse_sublines_optional(document
, 1);
809 return skin_parse_line_optional(document
, 1);
813 /* Memory management */
814 struct skin_element
* skin_alloc_element()
819 char* retval
= &skin_parse_tree
[skin_current_block
* 4];
821 int delta
= sizeof(struct skin_element
) / (sizeof(char) * 4);
823 /* If one block is partially filled, make sure to advance to the
824 * next one for the next allocation
826 if(sizeof(struct skin_element
) % (sizeof(char) * 4) != 0)
829 skin_current_block
+= delta
;
831 return (struct skin_element
*)retval
;
835 struct skin_element
* retval
= (struct skin_element
*)
836 malloc(sizeof(struct skin_element
));
838 retval
->params_count
= 0;
839 retval
->children_count
= 0;
845 struct skin_tag_parameter
* skin_alloc_params(int count
)
849 char* retval
= &skin_parse_tree
[skin_current_block
* 4];
851 int delta
= sizeof(struct skin_tag_parameter
) / (sizeof(char) * 4);
854 /* Correcting uneven alignment */
855 if(count
* sizeof(struct skin_tag_parameter
) % (sizeof(char) * 4) != 0)
858 skin_current_block
+= delta
;
860 return (struct skin_tag_parameter
*) retval
;
864 return (struct skin_tag_parameter
*)malloc(sizeof(struct skin_tag_parameter
)
869 char* skin_alloc_string(int length
)
873 char* retval
= &skin_parse_tree
[skin_current_block
* 4];
875 /* Checking alignment */
876 length
++; /* Accounting for the null terminator */
877 int delta
= length
/ 4;
881 skin_current_block
+= delta
;
883 if(skin_current_block
>= SKIN_MAX_MEMORY
/ 4)
884 skin_error(MEMORY_LIMIT_EXCEEDED
);
890 return (char*)malloc(sizeof(char) * (length
+ 1));
894 struct skin_element
** skin_alloc_children(int count
)
896 return (struct skin_element
**) malloc(sizeof(struct skin_element
*) * count
);
899 void skin_free_tree(struct skin_element
* root
)
903 /* First make the recursive call */
906 skin_free_tree(root
->next
);
909 if(root
->type
== TEXT
)
912 /* Then recursively free any children, before freeing their pointers */
913 for(i
= 0; i
< root
->children_count
; i
++)
914 skin_free_tree(root
->children
[i
]);
915 if(root
->children_count
> 0)
916 free(root
->children
);
918 /* Free any parameters, making sure to deallocate strings */
919 for(i
= 0; i
< root
->params_count
; i
++)
920 if(root
->params
[i
].type
== STRING
)
921 free(root
->params
[i
].data
.text
);
922 if(root
->params_count
> 0)
925 /* Finally, delete root's memory */