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_newline(struct skin_element
* element
, char** document
);
53 int skin_parse_comment(struct skin_element
* element
, char** document
);
54 struct skin_element
* skin_parse_code_as_arg(char** document
);
56 struct skin_element
* skin_parse(char* document
)
59 struct skin_element
* root
= NULL
;
60 struct skin_element
* last
= NULL
;
62 struct skin_element
** to_write
= 0;
64 char* cursor
= document
; /* Keeps track of location in the document */
68 while(*cursor
!= '\0')
74 to_write
= &(last
->next
);
77 *to_write
= skin_parse_viewport(&cursor
);
82 /* Making sure last is at the end */
92 struct skin_element
* skin_parse_viewport(char** document
)
95 struct skin_element
* root
= NULL
;
96 struct skin_element
* last
= NULL
;
97 struct skin_element
* retval
= NULL
;
99 retval
= skin_alloc_element();
100 retval
->type
= VIEWPORT
;
101 retval
->children_count
= 1;
102 retval
->line
= skin_line
;
104 struct skin_element
** to_write
= 0;
106 char* cursor
= *document
; /* Keeps track of location in the document */
107 char* bookmark
; /* Used when we need to look ahead */
109 int sublines
= 0; /* Flag for parsing sublines */
111 /* Parsing out the viewport tag if there is one */
112 if(check_viewport(cursor
))
114 retval
->children_count
= 2;
115 retval
->children
= skin_alloc_children(2);
116 retval
->children
[0] = skin_alloc_element();
117 skin_parse_tag(retval
->children
[0], &cursor
);
121 retval
->children_count
= 1;
122 retval
->children
= skin_alloc_children(1);
126 while(*cursor
!= '\0' && !(check_viewport(cursor
) && cursor
!= *document
))
129 /* First, we check to see if this line will contain sublines */
132 while(*cursor
!= '\n' && *cursor
!= '\0'
133 && !(check_viewport(cursor
) && cursor
!= *document
))
135 if(*cursor
== MULTILINESYM
)
140 else if(*cursor
== TAGSYM
)
142 /* A ';' directly after a '%' doesn't count */
150 else if(*cursor
== COMMENTSYM
)
152 skip_comment(&cursor
);
156 /* Advancing the cursor as normal */
165 to_write
= &(last
->next
);
169 *to_write
= skin_alloc_element();
170 skin_parse_newline(*to_write
, &cursor
);
176 *to_write
= skin_parse_sublines(&cursor
);
184 *to_write
= skin_parse_line(&cursor
);
191 /* Making sure last is at the end */
199 retval
->children
[retval
->children_count
- 1] = root
;
204 /* Auxiliary Parsing Functions */
206 struct skin_element
* skin_parse_line(char**document
)
209 return skin_parse_line_optional(document
, 0);
215 * If conditional is set to true, then this will break upon encountering
216 * SEPERATESYM. This should only be used when parsing a line inside a
217 * conditional, otherwise just use the wrapper function skin_parse_line()
219 struct skin_element
* skin_parse_line_optional(char** document
, int conditional
)
221 char* cursor
= *document
;
223 struct skin_element
* root
= NULL
;
224 struct skin_element
* current
= NULL
;
225 struct skin_element
* retval
= NULL
;
227 /* A wrapper for the line */
228 retval
= skin_alloc_element();
230 retval
->line
= skin_line
;
231 retval
->children_count
= 1;
232 retval
->children
= skin_alloc_children(1);
234 while(*cursor
!= '\n' && *cursor
!= '\0' && *cursor
!= MULTILINESYM
235 && !((*cursor
== ARGLISTSEPERATESYM
236 || *cursor
== ARGLISTCLOSESYM
237 || *cursor
== ENUMLISTSEPERATESYM
238 || *cursor
== ENUMLISTCLOSESYM
)
240 && !(check_viewport(cursor
) && cursor
!= *document
))
242 /* Allocating memory if necessary */
245 current
->next
= skin_alloc_element();
246 current
= current
->next
;
250 current
= skin_alloc_element();
254 /* Parsing the current element */
255 if(*cursor
== TAGSYM
&& cursor
[1] == CONDITIONSYM
)
257 if(!skin_parse_conditional(current
, &cursor
))
260 else if(*cursor
== TAGSYM
)
262 if(!skin_parse_tag(current
, &cursor
))
265 else if(*cursor
== COMMENTSYM
)
267 if(!skin_parse_comment(current
, &cursor
))
272 if(!skin_parse_text(current
, &cursor
, conditional
))
277 /* Moving up the calling function's pointer */
280 retval
->children
[0] = root
;
284 struct skin_element
* skin_parse_sublines(char** document
)
286 return skin_parse_sublines_optional(document
, 0);
289 struct skin_element
* skin_parse_sublines_optional(char** document
,
292 struct skin_element
* retval
;
293 char* cursor
= *document
;
297 retval
= skin_alloc_element();
298 retval
->type
= SUBLINES
;
300 retval
->line
= skin_line
;
302 /* First we count the sublines */
303 while(*cursor
!= '\0' && *cursor
!= '\n'
304 && !((*cursor
== ARGLISTSEPERATESYM
305 || *cursor
== ARGLISTCLOSESYM
306 || *cursor
== ENUMLISTSEPERATESYM
307 || *cursor
== ENUMLISTCLOSESYM
)
309 && !(check_viewport(cursor
) && cursor
!= *document
))
311 if(*cursor
== COMMENTSYM
)
312 skip_comment(&cursor
);
314 /* Accounting for escaped subline symbols */
315 if(*cursor
== TAGSYM
)
318 if(*cursor
== '\0' || *cursor
== '\n')
322 if(*cursor
== MULTILINESYM
)
330 /* ...and then we parse them */
331 retval
->children_count
= sublines
;
332 retval
->children
= skin_alloc_children(sublines
);
335 for(i
= 0; i
< sublines
; i
++)
337 retval
->children
[i
] = skin_parse_line_optional(&cursor
, conditional
);
338 skip_whitespace(&cursor
);
340 if(*cursor
!= MULTILINESYM
&& i
!= sublines
- 1)
342 skin_error(MULTILINE_EXPECTED
);
345 else if(i
!= sublines
- 1)
356 int skin_parse_tag(struct skin_element
* element
, char** document
)
359 char* cursor
= *document
+ 1;
364 struct tag_info
*tag
;
368 int star
= 0; /* Flag for the all-or-none option */
369 int req_args
; /* To mark when we enter optional arguments */
373 /* Checking the tag name */
374 tag_name
[0] = cursor
[0];
375 tag_name
[1] = cursor
[1];
378 /* First we check the two characters after the '%', then a single char */
379 tag
= find_tag(tag_name
);
384 tag
= find_tag(tag_name
);
394 skin_error(ILLEGAL_TAG
);
398 /* Copying basic tag info */
401 tag_args
= tag
->params
;
402 element
->line
= skin_line
;
404 /* Checking for the * flag */
405 if(tag_args
[0] == '*')
411 /* If this tag has no arguments, we can bail out now */
412 if(strlen(tag_args
) == 0
413 || (tag_args
[0] == '|' && *cursor
!= ARGLISTOPENSYM
))
419 /* Checking the number of arguments and allocating args */
420 if(*cursor
!= ARGLISTOPENSYM
&& tag_args
[0] != '|')
422 skin_error(ARGLIST_EXPECTED
);
430 for(bookmark
= cursor
; *cursor
!= '\n' && *cursor
!= '\0' &&
431 *cursor
!= ARGLISTCLOSESYM
; cursor
++)
433 /* Skipping over escaped characters */
434 if(*cursor
== TAGSYM
)
441 /* Skipping comments */
442 if(*cursor
== COMMENTSYM
)
443 skip_comment(&cursor
);
445 /* Commas inside of contained lists don't count */
446 if(*cursor
== ARGLISTOPENSYM
)
447 while(*cursor
!= ARGLISTCLOSESYM
&& *cursor
!= '\0')
450 if(*cursor
== ARGLISTSEPERATESYM
)
454 cursor
= bookmark
; /* Restoring the cursor */
455 element
->params_count
= num_args
;
456 element
->params
= skin_alloc_params(num_args
);
458 /* Now we have to actually parse each argument */
459 for(i
= 0; i
< num_args
; i
++)
461 /* Making sure we haven't run out of arguments */
462 if(*tag_args
== '\0')
464 skin_error(TOO_MANY_ARGS
);
468 /* Checking for the optional bar */
476 /* Scanning the arguments */
477 skip_whitespace(&cursor
);
480 /* Checking for comments */
481 if(*cursor
== COMMENTSYM
)
482 skip_comment(&cursor
);
484 /* Checking a nullable argument for null */
485 if(*cursor
== DEFAULTSYM
)
487 if(islower(*tag_args
))
489 element
->params
[i
].type
= DEFAULT
;
494 skin_error(DEFAULT_NOT_ALLOWED
);
498 else if(tolower(*tag_args
) == 'i')
500 /* Scanning an int argument */
501 if(!isdigit(*cursor
))
503 skin_error(INT_EXPECTED
);
507 element
->params
[i
].type
= NUMERIC
;
508 element
->params
[i
].data
.numeric
= scan_int(&cursor
);
510 else if(tolower(*tag_args
) == 's' || tolower(*tag_args
) == 'f')
512 /* Scanning a string argument */
513 element
->params
[i
].type
= STRING
;
514 element
->params
[i
].data
.text
= scan_string(&cursor
);
517 else if(tolower(*tag_args
) == 'c')
519 /* Recursively parsing a code argument */
520 element
->params
[i
].type
= CODE
;
521 element
->params
[i
].data
.code
= skin_parse_code_as_arg(&cursor
);
522 if(!element
->params
[i
].data
.code
)
526 skip_whitespace(&cursor
);
528 if(*cursor
!= ARGLISTSEPERATESYM
&& i
< num_args
- 1)
530 skin_error(SEPERATOR_EXPECTED
);
533 else if(*cursor
!= ARGLISTCLOSESYM
&& i
== num_args
- 1)
535 skin_error(CLOSE_EXPECTED
);
545 /* Checking for the optional bar */
555 /* Checking for a premature end */
556 if(*tag_args
!= '\0' && !(optional
&& (!star
|| num_args
== req_args
)))
558 skin_error(INSUFFICIENT_ARGS
);
568 * If the conditional flag is set true, then parsing text will stop at an
569 * ARGLISTSEPERATESYM. Only set that flag when parsing within a conditional
571 int skin_parse_text(struct skin_element
* element
, char** document
,
574 char* cursor
= *document
;
580 /* First figure out how much text we're copying */
581 while(*cursor
!= '\0' && *cursor
!= '\n' && *cursor
!= MULTILINESYM
582 && *cursor
!= COMMENTSYM
583 && !((*cursor
== ARGLISTSEPERATESYM
584 || *cursor
== ARGLISTCLOSESYM
585 || *cursor
== ENUMLISTSEPERATESYM
586 || *cursor
== ENUMLISTCLOSESYM
)
589 /* Dealing with possibility of escaped characters */
590 if(*cursor
== TAGSYM
)
592 if(find_escape_character(cursor
[1]))
604 /* Copying the text into the element struct */
605 element
->type
= TEXT
;
606 element
->line
= skin_line
;
607 element
->next
= NULL
;
608 element
->text
= skin_alloc_string(length
);
610 for(dest
= 0; dest
< length
; dest
++)
612 /* Advancing cursor if we've encountered an escaped character */
613 if(*cursor
== TAGSYM
)
616 element
->text
[dest
] = *cursor
;
619 element
->text
[length
] = '\0';
626 int skin_parse_conditional(struct skin_element
* element
, char** document
)
629 char* cursor
= *document
+ 1; /* Starting past the "%" */
631 struct skin_element
* tag
= skin_alloc_element(); /* The tag to evaluate */
635 element
->type
= CONDITIONAL
;
636 element
->line
= skin_line
;
638 /* Parsing the tag first */
639 if(!skin_parse_tag(tag
, &cursor
))
642 /* Counting the children */
643 if(*(cursor
++) != ENUMLISTOPENSYM
)
645 skin_error(ARGLIST_EXPECTED
);
649 while(*cursor
!= ENUMLISTCLOSESYM
&& *cursor
!= '\n' && *cursor
!= '\0')
651 if(*cursor
== COMMENTSYM
)
653 skip_comment(&cursor
);
657 if(*cursor
== TAGSYM
)
660 if(*cursor
== '\0' || *cursor
== '\n')
666 if(*cursor
== ENUMLISTSEPERATESYM
)
673 /* Parsing the children */
674 element
->children_count
= children
+ 1; /* Make sure to include the tag */
675 element
->children
= skin_alloc_children(children
+ 1);
676 element
->children
[0] = tag
;
678 for(i
= 1; i
< children
+ 1; i
++)
680 element
->children
[i
] = skin_parse_code_as_arg(&cursor
);
681 skip_whitespace(&cursor
);
683 if(i
< children
&& *cursor
!= ENUMLISTSEPERATESYM
)
685 skin_error(SEPERATOR_EXPECTED
);
688 else if(i
== children
&& *cursor
!= ENUMLISTCLOSESYM
)
690 skin_error(CLOSE_EXPECTED
);
704 int skin_parse_newline(struct skin_element
* element
, char** document
)
707 char* cursor
= *document
;
710 skin_error(NEWLINE_EXPECTED
);
715 /* Assembling a skin_element struct for a newline */
716 element
->type
= NEWLINE
;
717 element
->line
= skin_line
;
719 element
->next
= NULL
;
726 int skin_parse_comment(struct skin_element
* element
, char** document
)
728 char* cursor
= *document
;
732 * Finding the index of the ending newline or null-terminator
733 * The length of the string of interest doesn't include the leading #, the
734 * length we need to reserve is the same as the index of the last character
736 for(length
= 0; cursor
[length
] != '\n' && cursor
[length
] != '\0'; length
++);
738 element
->type
= COMMENT
;
739 element
->line
= skin_line
;
740 element
->text
= skin_alloc_string(length
);
741 /* We copy from one char past cursor to leave out the # */
742 memcpy((void*)(element
->text
), (void*)(cursor
+ 1), sizeof(char) * length
);
743 element
->text
[length
] = '\0';
745 if(cursor
[length
] == '\n')
748 *document
+= (length
+ 1); /* Move cursor up past # and all text */
753 struct skin_element
* skin_parse_code_as_arg(char** document
)
757 char* cursor
= *document
;
759 /* Checking for sublines */
760 while(*cursor
!= '\n' && *cursor
!= '\0')
762 if(*cursor
== MULTILINESYM
)
767 else if(*cursor
== TAGSYM
)
769 /* A ';' directly after a '%' doesn't count */
779 /* Advancing the cursor as normal */
785 return skin_parse_sublines_optional(document
, 1);
787 return skin_parse_line_optional(document
, 1);
791 /* Memory management */
792 struct skin_element
* skin_alloc_element()
797 char* retval
= &skin_parse_tree
[skin_current_block
* 4];
799 int delta
= sizeof(struct skin_element
) / (sizeof(char) * 4);
801 /* If one block is partially filled, make sure to advance to the
802 * next one for the next allocation
804 if(sizeof(struct skin_element
) % (sizeof(char) * 4) != 0)
807 skin_current_block
+= delta
;
809 return (struct skin_element
*)retval
;
813 struct skin_element
* retval
= (struct skin_element
*)
814 malloc(sizeof(struct skin_element
));
816 retval
->params_count
= 0;
817 retval
->children_count
= 0;
823 struct skin_tag_parameter
* skin_alloc_params(int count
)
827 char* retval
= &skin_parse_tree
[skin_current_block
* 4];
829 int delta
= sizeof(struct skin_tag_parameter
) / (sizeof(char) * 4);
832 /* Correcting uneven alignment */
833 if(count
* sizeof(struct skin_tag_parameter
) % (sizeof(char) * 4) != 0)
836 skin_current_block
+= delta
;
838 return (struct skin_tag_parameter
*) retval
;
842 return (struct skin_tag_parameter
*)malloc(sizeof(struct skin_tag_parameter
)
847 char* skin_alloc_string(int length
)
851 char* retval
= &skin_parse_tree
[skin_current_block
* 4];
853 /* Checking alignment */
854 length
++; /* Accounting for the null terminator */
855 int delta
= length
/ 4;
859 skin_current_block
+= delta
;
861 if(skin_current_block
>= SKIN_MAX_MEMORY
/ 4)
862 skin_error(MEMORY_LIMIT_EXCEEDED
);
868 return (char*)malloc(sizeof(char) * (length
+ 1));
872 struct skin_element
** skin_alloc_children(int count
)
874 return (struct skin_element
**) malloc(sizeof(struct skin_element
*) * count
);
877 void skin_free_tree(struct skin_element
* root
)
881 /* First make the recursive call */
884 skin_free_tree(root
->next
);
887 if(root
->type
== TEXT
)
890 /* Then recursively free any children, before freeing their pointers */
891 for(i
= 0; i
< root
->children_count
; i
++)
892 skin_free_tree(root
->children
[i
]);
893 if(root
->children_count
> 0)
894 free(root
->children
);
896 /* Free any parameters, making sure to deallocate strings */
897 for(i
= 0; i
< root
->params_count
; i
++)
898 if(root
->params
[i
].type
== STRING
)
899 free(root
->params
[i
].data
.text
);
900 if(root
->params_count
> 0)
903 /* Finally, delete root's memory */