Implemented quoted string options, comments and empty lines.
[liboconfig.git] / src / parser.y
1 %{
2 #include <stdlib.h>
3 #include <string.h>
4 #include "oconfig.h"
5
6 struct statement_list_s
7 {
8         oconfig_item_t *statement;
9         int             statement_num;
10 };
11 typedef struct statement_list_s statement_list_t;
12
13 struct argument_list_s
14 {
15         oconfig_value_t *argument;
16         int              argument_num;
17 };
18 typedef struct argument_list_s argument_list_t;
19
20 static char *unquote (const char *orig);
21 static void dump_ci (oconfig_item_t *ci, int shift);
22 %}
23
24 %start entire_file
25
26 %union {
27         double  number;
28         int     boolean;
29         char   *string;
30         oconfig_value_t  cv;
31         oconfig_item_t   ci;
32         argument_list_t  al;
33         statement_list_t sl;
34 }
35
36 %token <number> NUMBER
37 %token <boolean> TRUE FALSE
38 %token <string> QUOTED_STRING UNQUOTED_STRING
39 %token SLASH OPENBRAC CLOSEBRAC EOL
40
41 %type <string> string
42 %type <string> identifier
43 /* arguments */
44 %type <cv> argument
45 %type <al> argument_list
46 /* blocks */
47 %type <ci> block_begin
48 %type <ci> block
49 %type <string> block_end
50 /* statements */
51 %type <ci> option
52 %type <ci> statement
53 %type <sl> statement_list
54
55 %%
56 string:
57         QUOTED_STRING           {$$ = unquote ($1);}
58         | UNQUOTED_STRING       {$$ = strdup ($1);}
59         ;
60
61 argument:
62         NUMBER                  {$$.value.number = $1; $$.type = OCONFIG_TYPE_NUMBER;}
63         | TRUE                  {$$.value.boolean = 1; $$.type = OCONFIG_TYPE_BOOLEAN;}
64         | FALSE                 {$$.value.boolean = 0; $$.type = OCONFIG_TYPE_BOOLEAN;}
65         | string                {$$.value.string = $1; $$.type = OCONFIG_TYPE_STRING;}
66         ;
67
68 argument_list:
69         argument_list argument
70         {
71          $$ = $1;
72          $$.argument_num++;
73          $$.argument = realloc ($$.argument, $$.argument_num * sizeof (oconfig_value_t));
74          $$.argument[$$.argument_num-1] = $2;
75         }
76         | argument
77         {
78          $$.argument = malloc (sizeof (oconfig_value_t));
79          $$.argument[0] = $1;
80          $$.argument_num = 1;
81         }
82         ;
83
84 identifier:
85         UNQUOTED_STRING                 {$$ = strdup ($1);}
86         ;
87
88 option:
89         identifier argument_list EOL
90         {
91          memset (&$$, '\0', sizeof ($$));
92          $$.key = $1;
93          $$.values = $2.argument;
94          $$.values_num = $2.argument_num;
95         }
96         ;
97
98 block_begin:
99         OPENBRAC identifier argument_list CLOSEBRAC EOL
100         {
101          memset (&$$, '\0', sizeof ($$));
102          $$.key = $2;
103          $$.values = $3.argument;
104          $$.values_num = $3.argument_num;
105         }
106         ;
107
108 block_end:
109         OPENBRAC SLASH identifier CLOSEBRAC EOL
110         {
111          $$ = $3;
112         }
113         ;
114
115 block:
116         block_begin statement_list block_end
117         {
118          if (strcmp ($1.key, $3) != 0)
119          {
120                 printf ("block_begin = %s; block_end = %s;\n", $1.key, $3);
121                 yyerror ("Block not closed..\n");
122                 exit (1);
123          }
124          $$ = $1;
125          $$.children = $2.statement;
126          $$.children_num = $2.statement_num;
127         }
128         ;
129
130 statement:
131         option          {$$ = $1;}
132         | block         {$$ = $1;}
133         | EOL           {$$.values_num = 0;}
134         ;
135
136 statement_list:
137         statement_list statement
138         {
139          $$ = $1;
140          if ($2.values_num > 0)
141          {
142                  $$.statement_num++;
143                  $$.statement = realloc ($$.statement, $$.statement_num * sizeof (oconfig_item_t));
144                  $$.statement[$$.statement_num-1] = $2;
145          }
146         }
147         | statement
148         {
149          if ($1.values_num > 0)
150          {
151                  $$.statement = malloc (sizeof (oconfig_item_t));
152                  $$.statement[0] = $1;
153                  $$.statement_num = 1;
154          }
155          else
156          {
157                 $$.statement = NULL;
158                 $$.statement_num = 0;
159          }
160         }
161         ;
162
163 entire_file:
164         statement_list
165         {
166          int i;
167          for (i = 0; i < $1.statement_num; i++)
168                 dump_ci ($1.statement + i, 0);
169         }
170         ;
171
172 %%
173 #include "lex.yy.c"
174
175 /*
176 void yyerror (char *s)
177 {
178         fprintf (stderr, "%s\n", s);
179 }
180
181 int yylex (void)
182 {
183         return (getc (stdin));
184 }
185 */
186
187 int main (int argc, char **argv)
188 {
189         yyparse ();
190         return (0);
191 }
192
193 static char *unquote (const char *orig)
194 {
195         char *ret = strdup (orig);
196         int len;
197         int i;
198
199         if (ret == NULL)
200                 return (NULL);
201
202         len = strlen (ret);
203
204         if ((len < 2) || (ret[0] != '"') || (ret[len - 1] != '"'))
205                 return (ret);
206
207         ret++;
208         len -= 2;
209         ret[len] = '\0';
210
211         for (i = 0; i < len; i++)
212         {
213                 if (ret[i] == '\\')
214                 {
215                         memmove (ret + i, ret + (i + 1), len - i);
216                         len--;
217                 }
218         }
219
220         return (ret);
221 } /* char *unquote */
222
223 static void dump_ci (oconfig_item_t *ci, int shift)
224 {
225         int i;
226
227         if (shift > 0)
228                 printf ("%*s", shift, "");
229
230         printf ("%s", ci->key);
231         for (i = 0; i < ci->values_num; i++)
232         {
233                 oconfig_value_t cv = ci->values[i];
234
235                 if (cv.type == OCONFIG_TYPE_STRING)
236                         printf (" `%s'", cv.value.string);
237                 else if (cv.type == OCONFIG_TYPE_NUMBER)
238                         printf (" %lf", cv.value.number);
239                 else if (cv.type == OCONFIG_TYPE_BOOLEAN)
240                         printf (" %s", cv.value.boolean ? "true" : "false");
241                 else
242                         printf ("<unknown type %i>", cv.type);
243         }
244         printf ("\n");
245
246         for (i = 0; i < ci->children_num; i++)
247                 dump_ci (ci->children + i, shift + 1);
248 }