Merge branch 'collectd-5.7' into collectd-5.8
[collectd.git] / src / liboconfig / parser.y
1 /**
2  * collectd - src/liboconfig/parser.y
3  * Copyright (C) 2007,2008  Florian Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian Forster <octo at collectd.org>
25  */
26
27 %{
28 #include <stdlib.h>
29 #include <string.h>
30 #include "oconfig.h"
31 #include "aux_types.h"
32
33 static char *unquote (const char *orig);
34 static void yyerror(const char *s);
35
36 /* Lexer variables */
37 extern int yylineno;
38 extern char *yytext;
39 extern int yylex (void);
40
41 extern oconfig_item_t *ci_root;
42 extern char           *c_file;
43 %}
44
45 %start entire_file
46
47 %union {
48         double  number;
49         int     boolean;
50         char   *string;
51         oconfig_value_t  cv;
52         oconfig_item_t   ci;
53         argument_list_t  al;
54         statement_list_t sl;
55 }
56
57 %token <number> NUMBER
58 %token <boolean> BTRUE BFALSE
59 %token <string> QUOTED_STRING UNQUOTED_STRING
60 %token SLASH OPENBRAC CLOSEBRAC EOL
61
62 %type <string> string
63 %type <string> identifier
64 /* arguments */
65 %type <cv> argument
66 %type <al> argument_list
67 /* blocks */
68 %type <ci> block_begin
69 %type <ci> block
70 %type <string> block_end
71 /* statements */
72 %type <ci> option
73 %type <ci> statement
74 %type <sl> statement_list
75 %type <ci> entire_file
76
77 /* pass an verbose, specific error message to yyerror() */
78 %error-verbose
79
80 %%
81 string:
82         QUOTED_STRING           {$$ = unquote ($1);}
83         | UNQUOTED_STRING       {$$ = strdup ($1);}
84         ;
85
86 argument:
87         NUMBER                  {$$.value.number = $1; $$.type = OCONFIG_TYPE_NUMBER;}
88         | BTRUE                 {$$.value.boolean = 1; $$.type = OCONFIG_TYPE_BOOLEAN;}
89         | BFALSE                {$$.value.boolean = 0; $$.type = OCONFIG_TYPE_BOOLEAN;}
90         | string                {$$.value.string = $1; $$.type = OCONFIG_TYPE_STRING;}
91         ;
92
93 argument_list:
94         argument_list argument
95         {
96          $$ = $1;
97          oconfig_value_t *tmp = realloc($$.argument,
98                                         ($$.argument_num+1) * sizeof(*$$.argument));
99          if (tmp == NULL) {
100            yyerror("realloc failed");
101            YYERROR;
102          }
103          $$.argument = tmp;
104          $$.argument[$$.argument_num] = $2;
105          $$.argument_num++;
106         }
107         | argument
108         {
109          $$.argument = calloc(1, sizeof(*$$.argument));
110          if ($$.argument == NULL) {
111            yyerror("calloc failed");
112            YYERROR;
113          }
114          $$.argument[0] = $1;
115          $$.argument_num = 1;
116         }
117         ;
118
119 identifier:
120         UNQUOTED_STRING                 {$$ = strdup ($1);}
121         ;
122
123 option:
124         identifier argument_list EOL
125         {
126          memset(&$$, 0, sizeof($$));
127          $$.key = $1;
128          $$.values = $2.argument;
129          $$.values_num = $2.argument_num;
130         }
131         ;
132
133 block_begin:
134         OPENBRAC identifier CLOSEBRAC EOL
135         {
136          memset(&$$, 0, sizeof($$));
137          $$.key = $2;
138         }
139         |
140         OPENBRAC identifier argument_list CLOSEBRAC EOL
141         {
142          memset(&$$, 0, sizeof($$));
143          $$.key = $2;
144          $$.values = $3.argument;
145          $$.values_num = $3.argument_num;
146         }
147         ;
148
149 block_end:
150         OPENBRAC SLASH identifier CLOSEBRAC EOL
151         {
152          $$ = $3;
153         }
154         ;
155
156 block:
157         block_begin statement_list block_end
158         {
159          if (strcmp($1.key, $3) != 0)
160          {
161                 printf("block_begin = %s; block_end = %s;\n", $1.key, $3);
162                 yyerror("block not closed");
163                 YYERROR;
164          }
165          free ($3); $3 = NULL;
166          $$ = $1;
167          $$.children = $2.statement;
168          $$.children_num = $2.statement_num;
169         }
170         | block_begin block_end
171         {
172          if (strcmp($1.key, $2) != 0)
173          {
174                 printf("block_begin = %s; block_end = %s;\n", $1.key, $2);
175                 yyerror("block not closed");
176                 YYERROR;
177          }
178          free ($2); $2 = NULL;
179          $$ = $1;
180          $$.children = NULL;
181          $$.children_num = 0;
182         }
183         ;
184
185 statement:
186         option          {$$ = $1;}
187         | block         {$$ = $1;}
188         | EOL           {$$.values_num = 0;}
189         ;
190
191 statement_list:
192         statement_list statement
193         {
194          $$ = $1;
195          if (($2.values_num > 0) || ($2.children_num > 0))
196          {
197                  oconfig_item_t *tmp = realloc($$.statement,
198                                                ($$.statement_num+1) * sizeof(*tmp));
199                  if (tmp == NULL) {
200                    yyerror("realloc failed");
201                    YYERROR;
202                  }
203                  $$.statement = tmp;
204                  $$.statement[$$.statement_num] = $2;
205                  $$.statement_num++;
206          }
207         }
208         | statement
209         {
210          if (($1.values_num > 0) || ($1.children_num > 0))
211          {
212                  $$.statement = calloc(1, sizeof(*$$.statement));
213                  if ($$.statement == NULL) {
214                    yyerror("calloc failed");
215                    YYERROR;
216                  }
217                  $$.statement[0] = $1;
218                  $$.statement_num = 1;
219          }
220          else
221          {
222                 $$.statement = NULL;
223                 $$.statement_num = 0;
224          }
225         }
226         ;
227
228 entire_file:
229         statement_list
230         {
231          ci_root = calloc(1, sizeof(*ci_root));
232          if (ci_root == NULL) {
233            yyerror("calloc failed");
234            YYERROR;
235          }
236          ci_root->children = $1.statement;
237          ci_root->children_num = $1.statement_num;
238         }
239         | /* epsilon */
240         {
241          ci_root = calloc(1, sizeof(*ci_root));
242          if (ci_root == NULL) {
243            yyerror("calloc failed");
244            YYERROR;
245          }
246         }
247         ;
248
249 %%
250 static void yyerror(const char *s)
251 {
252         const char *text;
253
254         if (yytext == NULL)
255                 text = "<empty>";
256         else if (*yytext == '\n')
257                 text = "<newline>";
258         else
259                 text = yytext;
260
261         fprintf(stderr, "Parse error in file `%s', line %i near `%s': %s\n",
262                 c_file, yylineno, text, s);
263 } /* int yyerror */
264
265 static char *unquote (const char *orig)
266 {
267         char *ret = strdup (orig);
268         int len;
269
270         if (ret == NULL)
271                 return (NULL);
272
273         len = strlen (ret);
274
275         if ((len < 2) || (ret[0] != '"') || (ret[len - 1] != '"'))
276                 return (ret);
277
278         len -= 2;
279         memmove (ret, ret + 1, len);
280         ret[len] = 0;
281
282         for (int i = 0; i < len; i++)
283         {
284                 if (ret[i] == '\\')
285                 {
286                         memmove (ret + i, ret + (i + 1), len - i);
287                         len--;
288                 }
289         }
290
291         return (ret);
292 } /* char *unquote */