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"
34 /* Declaration of parse tree buffer */
35 #define SKIN_MAX_MEMORY (30*1024)
36 static char skin_parse_tree
[SKIN_MAX_MEMORY
];
37 static char *skin_buffer
;
40 /* Global variables for the parser */
43 /* Auxiliary parsing functions (not visible at global scope) */
44 static struct skin_element
* skin_parse_viewport(char** document
);
45 static struct skin_element
* skin_parse_line(char** document
);
46 static struct skin_element
* skin_parse_line_optional(char** document
,
48 static struct skin_element
* skin_parse_sublines(char** document
);
49 static struct skin_element
* skin_parse_sublines_optional(char** document
,
52 static int skin_parse_tag(struct skin_element
* element
, char** document
);
53 static int skin_parse_text(struct skin_element
* element
, char** document
,
55 static int skin_parse_conditional(struct skin_element
* element
,
57 static int skin_parse_comment(struct skin_element
* element
, char** document
);
58 static struct skin_element
* skin_parse_code_as_arg(char** document
);
60 struct skin_element
* skin_parse(const char* document
)
63 struct skin_element
* root
= NULL
;
64 struct skin_element
* last
= NULL
;
66 struct skin_element
** to_write
= 0;
68 char* cursor
= (char*)document
; /*Keeps track of location in the document*/
71 skin_buffer
= &skin_parse_tree
[0];
78 while(*cursor
!= '\0')
84 to_write
= &(last
->next
);
87 *to_write
= skin_parse_viewport(&cursor
);
91 skin_free_tree(root
); /* Clearing any memory already used */
95 /* Making sure last is at the end */
105 static struct skin_element
* skin_parse_viewport(char** document
)
108 struct skin_element
* root
= NULL
;
109 struct skin_element
* last
= NULL
;
110 struct skin_element
* retval
= NULL
;
112 retval
= skin_alloc_element();
113 retval
->type
= VIEWPORT
;
114 retval
->children_count
= 1;
115 retval
->line
= skin_line
;
117 struct skin_element
** to_write
= 0;
119 char* cursor
= *document
; /* Keeps track of location in the document */
120 char* bookmark
; /* Used when we need to look ahead */
122 int sublines
= 0; /* Flag for parsing sublines */
124 /* Parsing out the viewport tag if there is one */
125 if(check_viewport(cursor
))
127 skin_parse_tag(retval
, &cursor
);
135 retval
->children_count
= 1;
136 retval
->children
= skin_alloc_children(1);
142 /* First, we check to see if this line will contain sublines */
145 while(*cursor
!= '\n' && *cursor
!= '\0'
146 && !(check_viewport(cursor
) && cursor
!= *document
))
148 if(*cursor
== MULTILINESYM
)
153 else if(*cursor
== TAGSYM
)
155 /* A ';' directly after a '%' doesn't count */
163 else if(*cursor
== COMMENTSYM
)
165 skip_comment(&cursor
);
167 else if(*cursor
== ARGLISTOPENSYM
)
169 skip_arglist(&cursor
);
171 else if(*cursor
== ENUMLISTOPENSYM
)
173 skip_enumlist(&cursor
);
177 /* Advancing the cursor as normal */
186 to_write
= &(last
->next
);
190 *to_write
= skin_parse_sublines(&cursor
);
198 *to_write
= skin_parse_line(&cursor
);
205 /* Making sure last is at the end */
215 while(*cursor
!= '\0' && !(check_viewport(cursor
) && cursor
!= *document
));
219 retval
->children
[0] = root
;
224 /* Auxiliary Parsing Functions */
226 static struct skin_element
* skin_parse_line(char**document
)
229 return skin_parse_line_optional(document
, 0);
235 * If conditional is set to true, then this will break upon encountering
236 * SEPERATESYM. This should only be used when parsing a line inside a
237 * conditional, otherwise just use the wrapper function skin_parse_line()
239 static struct skin_element
* skin_parse_line_optional(char** document
,
242 char* cursor
= *document
;
244 struct skin_element
* root
= NULL
;
245 struct skin_element
* current
= NULL
;
246 struct skin_element
* retval
= NULL
;
248 /* A wrapper for the line */
249 retval
= skin_alloc_element();
251 retval
->line
= skin_line
;
252 if(*cursor
!= '\0' && *cursor
!= '\n'
253 && !(conditional
&& (*cursor
== ARGLISTSEPERATESYM
254 || *cursor
== ARGLISTCLOSESYM
255 || *cursor
== ENUMLISTSEPERATESYM
256 || *cursor
== ENUMLISTCLOSESYM
)))
258 retval
->children_count
= 1;
262 retval
->children_count
= 0;
265 if(retval
->children_count
> 0)
266 retval
->children
= skin_alloc_children(1);
268 while(*cursor
!= '\n' && *cursor
!= '\0' && *cursor
!= MULTILINESYM
269 && !((*cursor
== ARGLISTSEPERATESYM
270 || *cursor
== ARGLISTCLOSESYM
271 || *cursor
== ENUMLISTSEPERATESYM
272 || *cursor
== ENUMLISTCLOSESYM
)
274 && !(check_viewport(cursor
) && cursor
!= *document
))
276 /* Allocating memory if necessary */
279 current
->next
= skin_alloc_element();
280 current
= current
->next
;
284 current
= skin_alloc_element();
288 /* Parsing the current element */
289 if(*cursor
== TAGSYM
&& cursor
[1] == CONDITIONSYM
)
291 if(!skin_parse_conditional(current
, &cursor
))
294 else if(*cursor
== TAGSYM
&& !find_escape_character(cursor
[1]))
296 if(!skin_parse_tag(current
, &cursor
))
299 else if(*cursor
== COMMENTSYM
)
301 if(!skin_parse_comment(current
, &cursor
))
306 if(!skin_parse_text(current
, &cursor
, conditional
))
311 /* Moving up the calling function's pointer */
315 retval
->children
[0] = root
;
319 static struct skin_element
* skin_parse_sublines(char** document
)
321 return skin_parse_sublines_optional(document
, 0);
324 static struct skin_element
* skin_parse_sublines_optional(char** document
,
327 struct skin_element
* retval
;
328 char* cursor
= *document
;
332 retval
= skin_alloc_element();
333 retval
->type
= SUBLINES
;
335 retval
->line
= skin_line
;
337 /* First we count the sublines */
338 while(*cursor
!= '\0' && *cursor
!= '\n'
339 && !((*cursor
== ARGLISTSEPERATESYM
340 || *cursor
== ARGLISTCLOSESYM
341 || *cursor
== ENUMLISTSEPERATESYM
342 || *cursor
== ENUMLISTCLOSESYM
)
344 && !(check_viewport(cursor
) && cursor
!= *document
))
346 if(*cursor
== COMMENTSYM
)
348 skip_comment(&cursor
);
350 else if(*cursor
== ENUMLISTOPENSYM
)
352 skip_enumlist(&cursor
);
354 else if(*cursor
== ARGLISTOPENSYM
)
356 skip_arglist(&cursor
);
358 else if(*cursor
== TAGSYM
)
361 if(*cursor
== '\0' || *cursor
== '\n')
365 else if(*cursor
== MULTILINESYM
)
376 /* ...and then we parse them */
377 retval
->children_count
= sublines
;
378 retval
->children
= skin_alloc_children(sublines
);
381 for(i
= 0; i
< sublines
; i
++)
383 retval
->children
[i
] = skin_parse_line_optional(&cursor
, conditional
);
384 skip_whitespace(&cursor
);
386 if(*cursor
!= MULTILINESYM
&& i
!= sublines
- 1)
388 skin_error(MULTILINE_EXPECTED
);
391 else if(i
!= sublines
- 1)
402 static int skin_parse_tag(struct skin_element
* element
, char** document
)
405 char* cursor
= *document
+ 1;
410 struct tag_info
*tag
;
414 int star
= 0; /* Flag for the all-or-none option */
415 int req_args
; /* To mark when we enter optional arguments */
419 /* Checking the tag name */
420 tag_name
[0] = cursor
[0];
421 tag_name
[1] = cursor
[1];
424 /* First we check the two characters after the '%', then a single char */
425 tag
= find_tag(tag_name
);
430 tag
= find_tag(tag_name
);
440 skin_error(ILLEGAL_TAG
);
444 /* Copying basic tag info */
445 if(element
->type
!= CONDITIONAL
&& element
->type
!= VIEWPORT
)
448 tag_args
= tag
->params
;
449 element
->line
= skin_line
;
451 /* Checking for the * flag */
452 if(tag_args
[0] == '*')
458 /* If this tag has no arguments, we can bail out now */
459 if(strlen(tag_args
) == 0
460 || (tag_args
[0] == '|' && *cursor
!= ARGLISTOPENSYM
)
461 || (star
&& *cursor
!= ARGLISTOPENSYM
))
467 /* Checking the number of arguments and allocating args */
468 if(*cursor
!= ARGLISTOPENSYM
&& tag_args
[0] != '|')
470 skin_error(ARGLIST_EXPECTED
);
479 while(*cursor
!= '\n' && *cursor
!= '\0' && *cursor
!= ARGLISTCLOSESYM
)
481 /* Skipping over escaped characters */
482 if(*cursor
== TAGSYM
)
489 else if(*cursor
== COMMENTSYM
)
491 skip_comment(&cursor
);
493 else if(*cursor
== ARGLISTOPENSYM
)
495 skip_arglist(&cursor
);
497 else if(*cursor
== ARGLISTSEPERATESYM
)
508 cursor
= bookmark
; /* Restoring the cursor */
509 element
->params_count
= num_args
;
510 element
->params
= skin_alloc_params(num_args
);
512 /* Now we have to actually parse each argument */
513 for(i
= 0; i
< num_args
; i
++)
515 /* Making sure we haven't run out of arguments */
516 if(*tag_args
== '\0')
518 skin_error(TOO_MANY_ARGS
);
522 /* Checking for the optional bar */
530 /* Scanning the arguments */
531 skip_whitespace(&cursor
);
534 /* Checking for comments */
535 if(*cursor
== COMMENTSYM
)
536 skip_comment(&cursor
);
538 /* Storing the type code */
539 element
->params
[i
].type_code
= *tag_args
;
541 /* Checking a nullable argument for null */
542 if(*cursor
== DEFAULTSYM
&& !isdigit(cursor
[1]))
544 if(islower(*tag_args
))
546 element
->params
[i
].type
= DEFAULT
;
551 skin_error(DEFAULT_NOT_ALLOWED
);
555 else if(tolower(*tag_args
) == 'i')
557 /* Scanning an int argument */
558 if(!isdigit(*cursor
) && *cursor
!= '-')
560 skin_error(INT_EXPECTED
);
564 element
->params
[i
].type
= NUMERIC
;
565 element
->params
[i
].data
.numeric
= scan_int(&cursor
);
567 else if(tolower(*tag_args
) == 'n' ||
568 tolower(*tag_args
) == 's' || tolower(*tag_args
) == 'f')
570 /* Scanning a string argument */
571 element
->params
[i
].type
= STRING
;
572 element
->params
[i
].data
.text
= scan_string(&cursor
);
575 else if(tolower(*tag_args
) == 'c')
577 /* Recursively parsing a code argument */
578 element
->params
[i
].type
= CODE
;
579 element
->params
[i
].data
.code
= skin_parse_code_as_arg(&cursor
);
580 if(!element
->params
[i
].data
.code
)
584 skip_whitespace(&cursor
);
586 if(*cursor
!= ARGLISTSEPERATESYM
&& i
< num_args
- 1)
588 skin_error(SEPERATOR_EXPECTED
);
591 else if(*cursor
!= ARGLISTCLOSESYM
&& i
== num_args
- 1)
593 skin_error(CLOSE_EXPECTED
);
601 if (*tag_args
!= 'N')
604 /* Checking for the optional bar */
614 /* Checking for a premature end */
615 if(*tag_args
!= '\0' && !optional
)
617 skin_error(INSUFFICIENT_ARGS
);
627 * If the conditional flag is set true, then parsing text will stop at an
628 * ARGLISTSEPERATESYM. Only set that flag when parsing within a conditional
630 static int skin_parse_text(struct skin_element
* element
, char** document
,
633 char* cursor
= *document
;
638 /* First figure out how much text we're copying */
639 while(*cursor
!= '\0' && *cursor
!= '\n' && *cursor
!= MULTILINESYM
640 && *cursor
!= COMMENTSYM
641 && !((*cursor
== ARGLISTSEPERATESYM
642 || *cursor
== ARGLISTCLOSESYM
643 || *cursor
== ENUMLISTSEPERATESYM
644 || *cursor
== ENUMLISTCLOSESYM
)
647 /* Dealing with possibility of escaped characters */
648 if(*cursor
== TAGSYM
)
650 if(find_escape_character(cursor
[1]))
662 /* Copying the text into the element struct */
663 element
->type
= TEXT
;
664 element
->line
= skin_line
;
665 element
->next
= NULL
;
666 element
->data
= text
= skin_alloc_string(length
);
668 for(dest
= 0; dest
< length
; dest
++)
670 /* Advancing cursor if we've encountered an escaped character */
671 if(*cursor
== TAGSYM
)
674 text
[dest
] = *cursor
;
684 static int skin_parse_conditional(struct skin_element
* element
, char** document
)
687 char* cursor
= *document
+ 1; /* Starting past the "%" */
692 element
->type
= CONDITIONAL
;
693 element
->line
= skin_line
;
695 /* Parsing the tag first */
696 if(!skin_parse_tag(element
, &cursor
))
699 /* Counting the children */
700 if(*(cursor
++) != ENUMLISTOPENSYM
)
702 skin_error(ARGLIST_EXPECTED
);
706 while(*cursor
!= ENUMLISTCLOSESYM
&& *cursor
!= '\n' && *cursor
!= '\0')
708 if(*cursor
== COMMENTSYM
)
710 skip_comment(&cursor
);
712 else if(*cursor
== ENUMLISTOPENSYM
)
714 skip_enumlist(&cursor
);
716 else if(*cursor
== TAGSYM
)
719 if(*cursor
== '\0' || *cursor
== '\n')
723 else if(*cursor
== ENUMLISTSEPERATESYM
)
735 /* Parsing the children */
736 element
->children
= skin_alloc_children(children
);
737 element
->children_count
= children
;
739 for(i
= 0; i
< children
; i
++)
741 element
->children
[i
] = skin_parse_code_as_arg(&cursor
);
742 skip_whitespace(&cursor
);
744 if(i
< children
- 1 && *cursor
!= ENUMLISTSEPERATESYM
)
746 skin_error(SEPERATOR_EXPECTED
);
749 else if(i
== children
- 1 && *cursor
!= ENUMLISTCLOSESYM
)
751 skin_error(CLOSE_EXPECTED
);
765 static int skin_parse_comment(struct skin_element
* element
, char** document
)
767 char* cursor
= *document
;
772 * Finding the index of the ending newline or null-terminator
773 * The length of the string of interest doesn't include the leading #, the
774 * length we need to reserve is the same as the index of the last character
776 for(length
= 0; cursor
[length
] != '\n' && cursor
[length
] != '\0'; length
++);
778 element
->type
= COMMENT
;
779 element
->line
= skin_line
;
781 element
->data
= NULL
;
783 element
->data
= text
= skin_alloc_string(length
);
784 /* We copy from one char past cursor to leave out the # */
785 memcpy((void*)text
, (void*)(cursor
+ 1),
786 sizeof(char) * (length
-1));
787 text
[length
- 1] = '\0';
789 if(cursor
[length
] == '\n')
792 *document
+= (length
); /* Move cursor up past # and all text */
793 if(**document
== '\n')
799 static struct skin_element
* skin_parse_code_as_arg(char** document
)
803 char* cursor
= *document
;
805 /* Checking for sublines */
806 while(*cursor
!= '\n' && *cursor
!= '\0'
807 && *cursor
!= ENUMLISTSEPERATESYM
&& *cursor
!= ARGLISTSEPERATESYM
808 && *cursor
!= ENUMLISTCLOSESYM
&& *cursor
!= ARGLISTCLOSESYM
)
810 if(*cursor
== MULTILINESYM
)
815 else if(*cursor
== TAGSYM
)
817 /* A ';' directly after a '%' doesn't count */
825 else if(*cursor
== ARGLISTOPENSYM
)
827 skip_arglist(&cursor
);
829 else if(*cursor
== ENUMLISTOPENSYM
)
831 skip_enumlist(&cursor
);
835 /* Advancing the cursor as normal */
841 return skin_parse_sublines_optional(document
, 1);
843 return skin_parse_line_optional(document
, 1);
847 /* Memory management */
848 char* skin_alloc(size_t size
)
851 char *retval
= skin_buffer
;
852 skin_buffer
= (void *)(((unsigned long)skin_buffer
+ 3) & ~3);
859 struct skin_element
* skin_alloc_element()
861 struct skin_element
* retval
= (struct skin_element
*)
862 skin_alloc(sizeof(struct skin_element
));
863 retval
->type
= UNKNOWN
;
866 retval
->params_count
= 0;
867 retval
->children_count
= 0;
873 struct skin_tag_parameter
* skin_alloc_params(int count
)
875 size_t size
= sizeof(struct skin_tag_parameter
) * count
;
876 return (struct skin_tag_parameter
*)skin_alloc(size
);
880 char* skin_alloc_string(int length
)
882 return (char*)skin_alloc(sizeof(char) * (length
+ 1));
885 struct skin_element
** skin_alloc_children(int count
)
887 return (struct skin_element
**)
888 skin_alloc(sizeof(struct skin_element
*) * count
);
891 void skin_free_tree(struct skin_element
* root
)
896 /* First make the recursive call */
899 skin_free_tree(root
->next
);
902 if(root
->type
== TEXT
|| root
->type
== COMMENT
)
905 /* Then recursively free any children, before freeing their pointers */
906 for(i
= 0; i
< root
->children_count
; i
++)
907 skin_free_tree(root
->children
[i
]);
908 if(root
->children_count
> 0)
909 free(root
->children
);
911 /* Free any parameters, making sure to deallocate strings */
912 for(i
= 0; i
< root
->params_count
; i
++)
913 if(root
->params
[i
].type
== STRING
)
914 free(root
->params
[i
].data
.text
);
915 if(root
->params_count
> 0)
918 /* Finally, delete root's memory */