summaryrefslogtreecommitdiff
path: root/lib/skin_parser/skin_parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/skin_parser/skin_parser.c')
-rw-r--r--lib/skin_parser/skin_parser.c923
1 files changed, 923 insertions, 0 deletions
diff --git a/lib/skin_parser/skin_parser.c b/lib/skin_parser/skin_parser.c
new file mode 100644
index 0000000000..93a71919bf
--- /dev/null
+++ b/lib/skin_parser/skin_parser.c
@@ -0,0 +1,923 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2010 Robert Bieber
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include <stdlib.h>
23#include <stdio.h>
24#include <string.h>
25#include <ctype.h>
26
27#include "skin_parser.h"
28#include "skin_debug.h"
29#include "tag_table.h"
30#include "symbols.h"
31#include "skin_scan.h"
32
33#ifdef ROCKBOX
34/* Declaration of parse tree buffer */
35#define SKIN_MAX_MEMORY (30*1024)
36static char skin_parse_tree[SKIN_MAX_MEMORY];
37static char *skin_buffer;
38#endif
39
40/* Global variables for the parser */
41int skin_line = 0;
42
43/* Auxiliary parsing functions (not visible at global scope) */
44static struct skin_element* skin_parse_viewport(char** document);
45static struct skin_element* skin_parse_line(char** document);
46static struct skin_element* skin_parse_line_optional(char** document,
47 int conditional);
48static struct skin_element* skin_parse_sublines(char** document);
49static struct skin_element* skin_parse_sublines_optional(char** document,
50 int conditional);
51
52static int skin_parse_tag(struct skin_element* element, char** document);
53static int skin_parse_text(struct skin_element* element, char** document,
54 int conditional);
55static int skin_parse_conditional(struct skin_element* element,
56 char** document);
57static int skin_parse_comment(struct skin_element* element, char** document);
58static struct skin_element* skin_parse_code_as_arg(char** document);
59
60struct skin_element* skin_parse(const char* document)
61{
62
63 struct skin_element* root = NULL;
64 struct skin_element* last = NULL;
65
66 struct skin_element** to_write = 0;
67
68 char* cursor = (char*)document; /*Keeps track of location in the document*/
69#ifdef ROCKBOX
70 /* FIXME */
71 skin_buffer = &skin_parse_tree[0];
72#endif
73
74 skin_line = 1;
75
76 skin_clear_errors();
77
78 while(*cursor != '\0')
79 {
80
81 if(!root)
82 to_write = &root;
83 else
84 to_write = &(last->next);
85
86
87 *to_write = skin_parse_viewport(&cursor);
88 last = *to_write;
89 if(!last)
90 {
91 skin_free_tree(root); /* Clearing any memory already used */
92 return NULL;
93 }
94
95 /* Making sure last is at the end */
96 while(last->next)
97 last = last->next;
98
99 }
100
101 return root;
102
103}
104
105static struct skin_element* skin_parse_viewport(char** document)
106{
107
108 struct skin_element* root = NULL;
109 struct skin_element* last = NULL;
110 struct skin_element* retval = NULL;
111
112 retval = skin_alloc_element();
113 retval->type = VIEWPORT;
114 retval->children_count = 1;
115 retval->line = skin_line;
116
117 struct skin_element** to_write = 0;
118
119 char* cursor = *document; /* Keeps track of location in the document */
120 char* bookmark; /* Used when we need to look ahead */
121
122 int sublines = 0; /* Flag for parsing sublines */
123
124 /* Parsing out the viewport tag if there is one */
125 if(check_viewport(cursor))
126 {
127 skin_parse_tag(retval, &cursor);
128 if(*cursor == '\n')
129 {
130 cursor++;
131 skin_line++;
132 }
133 }
134
135 retval->children_count = 1;
136 retval->children = skin_alloc_children(1);
137
138
139 do
140 {
141
142 /* First, we check to see if this line will contain sublines */
143 bookmark = cursor;
144 sublines = 0;
145 while(*cursor != '\n' && *cursor != '\0'
146 && !(check_viewport(cursor) && cursor != *document))
147 {
148 if(*cursor == MULTILINESYM)
149 {
150 sublines = 1;
151 break;
152 }
153 else if(*cursor == TAGSYM)
154 {
155 /* A ';' directly after a '%' doesn't count */
156 cursor ++;
157
158 if(*cursor == '\0')
159 break;
160
161 cursor++;
162 }
163 else if(*cursor == COMMENTSYM)
164 {
165 skip_comment(&cursor);
166 }
167 else if(*cursor == ARGLISTOPENSYM)
168 {
169 skip_arglist(&cursor);
170 }
171 else if(*cursor == ENUMLISTOPENSYM)
172 {
173 skip_enumlist(&cursor);
174 }
175 else
176 {
177 /* Advancing the cursor as normal */
178 cursor++;
179 }
180 }
181 cursor = bookmark;
182
183 if(!root)
184 to_write = &root;
185 else
186 to_write = &(last->next);
187
188 if(sublines)
189 {
190 *to_write = skin_parse_sublines(&cursor);
191 last = *to_write;
192 if(!last)
193 return NULL;
194 }
195 else
196 {
197
198 *to_write = skin_parse_line(&cursor);
199 last = *to_write;
200 if(!last)
201 return NULL;
202
203 }
204
205 /* Making sure last is at the end */
206 while(last->next)
207 last = last->next;
208
209 if(*cursor == '\n')
210 {
211 cursor++;
212 skin_line++;
213 }
214 }
215 while(*cursor != '\0' && !(check_viewport(cursor) && cursor != *document));
216
217 *document = cursor;
218
219 retval->children[0] = root;
220 return retval;
221
222}
223
224/* Auxiliary Parsing Functions */
225
226static struct skin_element* skin_parse_line(char**document)
227{
228
229 return skin_parse_line_optional(document, 0);
230
231}
232
233
234/*
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()
238 */
239static struct skin_element* skin_parse_line_optional(char** document,
240 int conditional)
241{
242 char* cursor = *document;
243
244 struct skin_element* root = NULL;
245 struct skin_element* current = NULL;
246 struct skin_element* retval = NULL;
247
248 /* A wrapper for the line */
249 retval = skin_alloc_element();
250 retval->type = LINE;
251 retval->line = skin_line;
252 if(*cursor != '\0' && *cursor != '\n'
253 && !(conditional && (*cursor == ARGLISTSEPERATESYM
254 || *cursor == ARGLISTCLOSESYM
255 || *cursor == ENUMLISTSEPERATESYM
256 || *cursor == ENUMLISTCLOSESYM)))
257 {
258 retval->children_count = 1;
259 }
260 else
261 {
262 retval->children_count = 0;
263 }
264
265 if(retval->children_count > 0)
266 retval->children = skin_alloc_children(1);
267
268 while(*cursor != '\n' && *cursor != '\0' && *cursor != MULTILINESYM
269 && !((*cursor == ARGLISTSEPERATESYM
270 || *cursor == ARGLISTCLOSESYM
271 || *cursor == ENUMLISTSEPERATESYM
272 || *cursor == ENUMLISTCLOSESYM)
273 && conditional)
274 && !(check_viewport(cursor) && cursor != *document))
275 {
276 /* Allocating memory if necessary */
277 if(root)
278 {
279 current->next = skin_alloc_element();
280 current = current->next;
281 }
282 else
283 {
284 current = skin_alloc_element();
285 root = current;
286 }
287
288 /* Parsing the current element */
289 if(*cursor == TAGSYM && cursor[1] == CONDITIONSYM)
290 {
291 if(!skin_parse_conditional(current, &cursor))
292 return NULL;
293 }
294 else if(*cursor == TAGSYM && !find_escape_character(cursor[1]))
295 {
296 if(!skin_parse_tag(current, &cursor))
297 return NULL;
298 }
299 else if(*cursor == COMMENTSYM)
300 {
301 if(!skin_parse_comment(current, &cursor))
302 return NULL;
303 }
304 else
305 {
306 if(!skin_parse_text(current, &cursor, conditional))
307 return NULL;
308 }
309 }
310
311 /* Moving up the calling function's pointer */
312 *document = cursor;
313
314 if(root)
315 retval->children[0] = root;
316 return retval;
317}
318
319static struct skin_element* skin_parse_sublines(char** document)
320{
321 return skin_parse_sublines_optional(document, 0);
322}
323
324static struct skin_element* skin_parse_sublines_optional(char** document,
325 int conditional)
326{
327 struct skin_element* retval;
328 char* cursor = *document;
329 int sublines = 1;
330 int i;
331
332 retval = skin_alloc_element();
333 retval->type = SUBLINES;
334 retval->next = NULL;
335 retval->line = skin_line;
336
337 /* First we count the sublines */
338 while(*cursor != '\0' && *cursor != '\n'
339 && !((*cursor == ARGLISTSEPERATESYM
340 || *cursor == ARGLISTCLOSESYM
341 || *cursor == ENUMLISTSEPERATESYM
342 || *cursor == ENUMLISTCLOSESYM)
343 && conditional)
344 && !(check_viewport(cursor) && cursor != *document))
345 {
346 if(*cursor == COMMENTSYM)
347 {
348 skip_comment(&cursor);
349 }
350 else if(*cursor == ENUMLISTOPENSYM)
351 {
352 skip_enumlist(&cursor);
353 }
354 else if(*cursor == ARGLISTOPENSYM)
355 {
356 skip_arglist(&cursor);
357 }
358 else if(*cursor == TAGSYM)
359 {
360 cursor++;
361 if(*cursor == '\0' || *cursor == '\n')
362 break;
363 cursor++;
364 }
365 else if(*cursor == MULTILINESYM)
366 {
367 sublines++;
368 cursor++;
369 }
370 else
371 {
372 cursor++;
373 }
374 }
375
376 /* ...and then we parse them */
377 retval->children_count = sublines;
378 retval->children = skin_alloc_children(sublines);
379
380 cursor = *document;
381 for(i = 0; i < sublines; i++)
382 {
383 retval->children[i] = skin_parse_line_optional(&cursor, conditional);
384 skip_whitespace(&cursor);
385
386 if(*cursor != MULTILINESYM && i != sublines - 1)
387 {
388 skin_error(MULTILINE_EXPECTED);
389 return NULL;
390 }
391 else if(i != sublines - 1)
392 {
393 cursor++;
394 }
395 }
396
397 *document = cursor;
398
399 return retval;
400}
401
402static int skin_parse_tag(struct skin_element* element, char** document)
403{
404
405 char* cursor = *document + 1;
406 char* bookmark;
407
408 char tag_name[3];
409 char* tag_args;
410 struct tag_info *tag;
411
412 int num_args = 1;
413 int i;
414 int star = 0; /* Flag for the all-or-none option */
415 int req_args; /* To mark when we enter optional arguments */
416
417 int optional = 0;
418
419 /* Checking the tag name */
420 tag_name[0] = cursor[0];
421 tag_name[1] = cursor[1];
422 tag_name[2] = '\0';
423
424 /* First we check the two characters after the '%', then a single char */
425 tag = find_tag(tag_name);
426
427 if(!tag)
428 {
429 tag_name[1] = '\0';
430 tag = find_tag(tag_name);
431 cursor++;
432 }
433 else
434 {
435 cursor += 2;
436 }
437
438 if(!tag)
439 {
440 skin_error(ILLEGAL_TAG);
441 return 0;
442 }
443
444 /* Copying basic tag info */
445 if(element->type != CONDITIONAL && element->type != VIEWPORT)
446 element->type = TAG;
447 element->tag = tag;
448 tag_args = tag->params;
449 element->line = skin_line;
450
451 /* Checking for the * flag */
452 if(tag_args[0] == '*')
453 {
454 star = 1;
455 tag_args++;
456 }
457
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))
462 {
463 *document = cursor;
464 return 1;
465 }
466
467 /* Checking the number of arguments and allocating args */
468 if(*cursor != ARGLISTOPENSYM && tag_args[0] != '|')
469 {
470 skin_error(ARGLIST_EXPECTED);
471 return 0;
472 }
473 else
474 {
475 cursor++;
476 }
477
478 bookmark = cursor;
479 while(*cursor != '\n' && *cursor != '\0' && *cursor != ARGLISTCLOSESYM)
480 {
481 /* Skipping over escaped characters */
482 if(*cursor == TAGSYM)
483 {
484 cursor++;
485 if(*cursor == '\0')
486 break;
487 cursor++;
488 }
489 else if(*cursor == COMMENTSYM)
490 {
491 skip_comment(&cursor);
492 }
493 else if(*cursor == ARGLISTOPENSYM)
494 {
495 skip_arglist(&cursor);
496 }
497 else if(*cursor == ARGLISTSEPERATESYM)
498 {
499 num_args++;
500 cursor++;
501 }
502 else
503 {
504 cursor++;
505 }
506 }
507
508 cursor = bookmark; /* Restoring the cursor */
509 element->params_count = num_args;
510 element->params = skin_alloc_params(num_args);
511
512 /* Now we have to actually parse each argument */
513 for(i = 0; i < num_args; i++)
514 {
515 /* Making sure we haven't run out of arguments */
516 if(*tag_args == '\0')
517 {
518 skin_error(TOO_MANY_ARGS);
519 return 0;
520 }
521
522 /* Checking for the optional bar */
523 if(*tag_args == '|')
524 {
525 optional = 1;
526 req_args = i;
527 tag_args++;
528 }
529
530 /* Scanning the arguments */
531 skip_whitespace(&cursor);
532
533
534 /* Checking for comments */
535 if(*cursor == COMMENTSYM)
536 skip_comment(&cursor);
537
538 /* Storing the type code */
539 element->params[i].type_code = *tag_args;
540
541 /* Checking a nullable argument for null */
542 if(*cursor == DEFAULTSYM && !isdigit(cursor[1]))
543 {
544 if(islower(*tag_args))
545 {
546 element->params[i].type = DEFAULT;
547 cursor++;
548 }
549 else
550 {
551 skin_error(DEFAULT_NOT_ALLOWED);
552 return 0;
553 }
554 }
555 else if(tolower(*tag_args) == 'i')
556 {
557 /* Scanning an int argument */
558 if(!isdigit(*cursor) && *cursor != '-')
559 {
560 skin_error(INT_EXPECTED);
561 return 0;
562 }
563
564 element->params[i].type = NUMERIC;
565 element->params[i].data.numeric = scan_int(&cursor);
566 }
567 else if(tolower(*tag_args) == 'n' ||
568 tolower(*tag_args) == 's' || tolower(*tag_args) == 'f')
569 {
570 /* Scanning a string argument */
571 element->params[i].type = STRING;
572 element->params[i].data.text = scan_string(&cursor);
573
574 }
575 else if(tolower(*tag_args) == 'c')
576 {
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)
581 return 0;
582 }
583
584 skip_whitespace(&cursor);
585
586 if(*cursor != ARGLISTSEPERATESYM && i < num_args - 1)
587 {
588 skin_error(SEPERATOR_EXPECTED);
589 return 0;
590 }
591 else if(*cursor != ARGLISTCLOSESYM && i == num_args - 1)
592 {
593 skin_error(CLOSE_EXPECTED);
594 return 0;
595 }
596 else
597 {
598 cursor++;
599 }
600
601 if (*tag_args != 'N')
602 tag_args++;
603
604 /* Checking for the optional bar */
605 if(*tag_args == '|')
606 {
607 optional = 1;
608 req_args = i + 1;
609 tag_args++;
610 }
611
612 }
613
614 /* Checking for a premature end */
615 if(*tag_args != '\0' && !optional)
616 {
617 skin_error(INSUFFICIENT_ARGS);
618 return 0;
619 }
620
621 *document = cursor;
622
623 return 1;
624}
625
626/*
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
629 */
630static int skin_parse_text(struct skin_element* element, char** document,
631 int conditional)
632{
633 char* cursor = *document;
634 int length = 0;
635 int dest;
636 char *text = NULL;
637
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)
645 && conditional))
646 {
647 /* Dealing with possibility of escaped characters */
648 if(*cursor == TAGSYM)
649 {
650 if(find_escape_character(cursor[1]))
651 cursor++;
652 else
653 break;
654 }
655
656 length++;
657 cursor++;
658 }
659
660 cursor = *document;
661
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);
667
668 for(dest = 0; dest < length; dest++)
669 {
670 /* Advancing cursor if we've encountered an escaped character */
671 if(*cursor == TAGSYM)
672 cursor++;
673
674 text[dest] = *cursor;
675 cursor++;
676 }
677 text[length] = '\0';
678
679 *document = cursor;
680
681 return 1;
682}
683
684static int skin_parse_conditional(struct skin_element* element, char** document)
685{
686
687 char* cursor = *document + 1; /* Starting past the "%" */
688 char* bookmark;
689 int children = 1;
690 int i;
691
692 element->type = CONDITIONAL;
693 element->line = skin_line;
694
695 /* Parsing the tag first */
696 if(!skin_parse_tag(element, &cursor))
697 return 0;
698
699 /* Counting the children */
700 if(*(cursor++) != ENUMLISTOPENSYM)
701 {
702 skin_error(ARGLIST_EXPECTED);
703 return 0;
704 }
705 bookmark = cursor;
706 while(*cursor != ENUMLISTCLOSESYM && *cursor != '\n' && *cursor != '\0')
707 {
708 if(*cursor == COMMENTSYM)
709 {
710 skip_comment(&cursor);
711 }
712 else if(*cursor == ENUMLISTOPENSYM)
713 {
714 skip_enumlist(&cursor);
715 }
716 else if(*cursor == TAGSYM)
717 {
718 cursor++;
719 if(*cursor == '\0' || *cursor == '\n')
720 break;
721 cursor++;
722 }
723 else if(*cursor == ENUMLISTSEPERATESYM)
724 {
725 children++;
726 cursor++;
727 }
728 else
729 {
730 cursor++;
731 }
732 }
733 cursor = bookmark;
734
735 /* Parsing the children */
736 element->children = skin_alloc_children(children);
737 element->children_count = children;
738
739 for(i = 0; i < children; i++)
740 {
741 element->children[i] = skin_parse_code_as_arg(&cursor);
742 skip_whitespace(&cursor);
743
744 if(i < children - 1 && *cursor != ENUMLISTSEPERATESYM)
745 {
746 skin_error(SEPERATOR_EXPECTED);
747 return 0;
748 }
749 else if(i == children - 1 && *cursor != ENUMLISTCLOSESYM)
750 {
751 skin_error(CLOSE_EXPECTED);
752 return 0;
753 }
754 else
755 {
756 cursor++;
757 }
758 }
759
760 *document = cursor;
761
762 return 1;
763}
764
765static int skin_parse_comment(struct skin_element* element, char** document)
766{
767 char* cursor = *document;
768 char* text = NULL;
769
770 int length;
771 /*
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
775 */
776 for(length = 0; cursor[length] != '\n' && cursor[length] != '\0'; length++);
777
778 element->type = COMMENT;
779 element->line = skin_line;
780#ifdef ROCKBOX
781 element->data = NULL;
782#else
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';
788#endif
789 if(cursor[length] == '\n')
790 skin_line++;
791
792 *document += (length); /* Move cursor up past # and all text */
793 if(**document == '\n')
794 (*document)++;
795
796 return 1;
797}
798
799static struct skin_element* skin_parse_code_as_arg(char** document)
800{
801
802 int sublines = 0;
803 char* cursor = *document;
804
805 /* Checking for sublines */
806 while(*cursor != '\n' && *cursor != '\0'
807 && *cursor != ENUMLISTSEPERATESYM && *cursor != ARGLISTSEPERATESYM
808 && *cursor != ENUMLISTCLOSESYM && *cursor != ARGLISTCLOSESYM)
809 {
810 if(*cursor == MULTILINESYM)
811 {
812 sublines = 1;
813 break;
814 }
815 else if(*cursor == TAGSYM)
816 {
817 /* A ';' directly after a '%' doesn't count */
818 cursor ++;
819
820 if(*cursor == '\0')
821 break;
822
823 cursor++;
824 }
825 else if(*cursor == ARGLISTOPENSYM)
826 {
827 skip_arglist(&cursor);
828 }
829 else if(*cursor == ENUMLISTOPENSYM)
830 {
831 skip_enumlist(&cursor);
832 }
833 else
834 {
835 /* Advancing the cursor as normal */
836 cursor++;
837 }
838 }
839
840 if(sublines)
841 return skin_parse_sublines_optional(document, 1);
842 else
843 return skin_parse_line_optional(document, 1);
844}
845
846
847/* Memory management */
848char* skin_alloc(size_t size)
849{
850#ifdef ROCKBOX
851 char *retval = skin_buffer;
852 skin_buffer = (void *)(((unsigned long)skin_buffer + 3) & ~3);
853 return retval;
854#else
855 return malloc(size);
856#endif
857}
858
859struct skin_element* skin_alloc_element()
860{
861 struct skin_element* retval = (struct skin_element*)
862 skin_alloc(sizeof(struct skin_element));
863 retval->type = UNKNOWN;
864 retval->next = NULL;
865 retval->tag = NULL;
866 retval->params_count = 0;
867 retval->children_count = 0;
868
869 return retval;
870
871}
872
873struct skin_tag_parameter* skin_alloc_params(int count)
874{
875 size_t size = sizeof(struct skin_tag_parameter) * count;
876 return (struct skin_tag_parameter*)skin_alloc(size);
877
878}
879
880char* skin_alloc_string(int length)
881{
882 return (char*)skin_alloc(sizeof(char) * (length + 1));
883}
884
885struct skin_element** skin_alloc_children(int count)
886{
887 return (struct skin_element**)
888 skin_alloc(sizeof(struct skin_element*) * count);
889}
890
891void skin_free_tree(struct skin_element* root)
892{
893#ifndef ROCKBOX
894 int i;
895
896 /* First make the recursive call */
897 if(!root)
898 return;
899 skin_free_tree(root->next);
900
901 /* Free any text */
902 if(root->type == TEXT || root->type == COMMENT)
903 free(root->data);
904
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);
910
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)
916 free(root->params);
917
918 /* Finally, delete root's memory */
919 free(root);
920#else
921 (void)root;
922#endif
923}