1 %{
2 /* grecs - Gray's Extensible Configuration System
3 Copyright (C) 2007-2016 Sergey Poznyakoff
4
5 Grecs is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 3 of the License, or (at your
8 option) any later version.
9
10 Grecs is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with Grecs. If not, see <http://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 #include <grecs.h>
22 #include <dhcpd-gram.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include <errno.h>
27
28 int yylex(void);
29 int yyerror(char const *s);
30
31 static struct grecs_node *parse_tree;
32 extern int yy_flex_debug;
33 extern int grecs_dhcpd_new_source(const char *name, grecs_locus_t *loc);
34 extern void grecs_dhcpd_close_sources(void);
35
36 extern void grecs_dhcpd_begin_bool(void);
37 extern void grecs_dhcpd_begin_expr(void);
38
39 /* NOTE: STRING must be allocated */
40 static grecs_value_t *
make_string_value(char * string)41 make_string_value(char *string)
42 {
43 grecs_value_t *val;
44 val = grecs_malloc(sizeof(val[0]));
45 val->type = GRECS_TYPE_STRING;
46 val->v.string = string;
47 return val;
48 }
49
50 %}
51
52 %error-verbose
53 %locations
54
55 %union {
56 char *string;
57 grecs_value_t svalue, *pvalue;
58 struct grecs_list *list;
59 struct grecs_node *node;
60 grecs_locus_t locus;
61 struct { struct grecs_node *head, *tail; } node_list;
62 }
63
64 %token <string> DHCPD_IF DHCPD_ELSIF DHCPD_EXPR
65 %token DHCPD_ELSE
66 %token <string> DHCPD_STRING DHCPD_IDENT
67 %type <string> string
68 %type <svalue> value
69 %type <pvalue> vallist tag
70 %type <list> vlist strlist
71 %type <node> stmt simple block maybe_stmtlist
72 %type <node> cond elsecond opt_elsecond elsifcond
73 %type <node_list> stmtlist elsifchain opt_elsifchain
74
75 %%
76
77 input : maybe_stmtlist
78 {
79 parse_tree = grecs_node_create(grecs_node_root, &@1);
80 parse_tree->v.texttab = grecs_text_table();
81 grecs_node_bind(parse_tree, $1, 1);
82 }
83 ;
84
85 maybe_stmtlist:
86 /* empty */
87 {
88 $$ = NULL;
89 }
90 | stmtlist
91 {
92 $$ = $1.head;
93 }
94 ;
95
96 stmtlist: stmt
97 {
98 $$.head = $$.tail = $1;
99 }
100 | stmtlist stmt
101 {
102 if ($2) {
103 if (!$1.head)
104 $1.head = $1.tail = $2;
105 else
106 grecs_node_bind($1.tail, $2, 0);
107 }
108 $$ = $1;
109 }
110 ;
111
112 stmt : simple
113 | block
114 | cond
115 ;
116
117 simple : DHCPD_IDENT vallist ';'
118 {
119 if (strcmp($1, "include") == 0 &&
120 $2->type == GRECS_TYPE_STRING) {
121 grecs_dhcpd_new_source($2->v.string, &@1);
122 $$ = NULL;
123 } else {
124 $$ = grecs_node_create_points(grecs_node_stmt,
125 @1.beg, @2.end);
126 $$->ident = $1;
127 $$->idloc = @1;
128 $$->v.value = $2;
129 }
130 }
131 | DHCPD_IDENT '=' { grecs_dhcpd_begin_expr(); } DHCPD_EXPR ';'
132 {
133 $$ = grecs_node_create_points(grecs_node_stmt,
134 @1.beg, @5.end);
135 $$->ident = $1;
136 $$->idloc = @1;
137 $$->v.value = make_string_value($4);
138 }
139 | string ';'
140 {
141 $$ = grecs_node_create(grecs_node_stmt, &@1);
142 $$->ident = $1;
143 $$->idloc = @1;
144 $$->v.value = NULL;
145 }
146 ;
147
148 block : DHCPD_IDENT tag '{' maybe_stmtlist '}' opt_semi
149 {
150 $$ = grecs_node_create_points(grecs_node_block,
151 @1.beg, @5.end);
152 $$->ident = $1;
153 $$->idloc = @1;
154 $$->v.value = $2;
155 grecs_node_bind($$, $4, 1);
156 }
157 ;
158
159 opt_semi: /* empty */
160 | ';'
161 ;
162
163 tag : /* empty */
164 {
165 $$ = NULL;
166 }
167 | vallist
168 ;
169
170 vallist : vlist
171 {
172 size_t n;
173
174 if ((n = grecs_list_size($1)) == 1) {
175 $$ = grecs_list_index($1, 0);
176 } else {
177 size_t i;
178 struct grecs_list_entry *ep;
179
180 $$ = grecs_malloc(sizeof($$[0]));
181 $$->type = GRECS_TYPE_ARRAY;
182 $$->locus = @1;
183 $$->v.arg.c = n;
184 $$->v.arg.v = grecs_calloc(n,
185 sizeof($$->v.arg.v[0]));
186 for (i = 0, ep = $1->head; ep; i++, ep = ep->next)
187 $$->v.arg.v[i] = ep->data;
188 }
189 $1->free_entry = NULL;
190 grecs_list_free($1);
191 }
192 ;
193
194 vlist : value
195 {
196 $$ = grecs_value_list_create();
197 grecs_list_append($$, grecs_value_ptr_from_static(&$1));
198 }
199 | vlist value
200 {
201 grecs_list_append($1, grecs_value_ptr_from_static(&$2));
202 }
203 ;
204
205 value : string
206 {
207 $$.type = GRECS_TYPE_STRING;
208 $$.locus = @1;
209 $$.v.string = $1;
210 }
211 | strlist
212 {
213 $$.type = GRECS_TYPE_LIST;
214 $$.locus = @1;
215 $$.v.list = $1;
216 }
217 ;
218
219 string : DHCPD_STRING
220 | DHCPD_IDENT
221 ;
222
223 strlist : DHCPD_STRING ',' DHCPD_STRING
224 {
225 grecs_value_t val;
226
227 $$ = grecs_value_list_create();
228
229 val.type = GRECS_TYPE_STRING;
230 val.locus = @1;
231 val.v.string = $1;
232 grecs_list_append($$, grecs_value_ptr_from_static(&val));
233
234 val.type = GRECS_TYPE_STRING;
235 val.locus = @3;
236 val.v.string = $3;
237 grecs_list_append($$, grecs_value_ptr_from_static(&val));
238 }
239 | strlist ',' DHCPD_STRING
240 {
241 grecs_value_t val;
242
243 val.type = GRECS_TYPE_STRING;
244 val.locus = @3;
245 val.v.string = $3;
246 grecs_list_append($1, grecs_value_ptr_from_static(&val));
247 }
248 ;
249
250 cond : if DHCPD_EXPR '{' maybe_stmtlist '}' opt_elsifchain opt_elsecond
251 {
252 $$ = grecs_node_create_points(grecs_node_block,
253 @1.beg, @7.end);
254
255 grecs_line_begin();
256 grecs_line_add("if", 2);
257
258 $$->ident = grecs_line_finish();
259 $$->idloc = @1;
260
261 $$->v.value = make_string_value ($2);
262 grecs_node_bind($$, $4, 1);
263
264 if ($6.head) {
265 grecs_node_bind($6.tail, $7, 0);
266 grecs_node_bind($$, $6.head, 0);
267 }
268 }
269 ;
270
271 if : DHCPD_IF
272 {
273 grecs_dhcpd_begin_bool();
274 }
275 ;
276
277 elsif : DHCPD_ELSIF
278 {
279 grecs_dhcpd_begin_bool();
280 }
281 ;
282
283 opt_elsifchain: /* empty */
284 {
285 $$.head = $$.tail = NULL;
286 }
287 | elsifchain
288 ;
289
290 elsifchain: elsifcond
291 {
292 $$.head = $$.tail = $1;
293 }
294 | elsifchain elsifcond
295 {
296 if ($2) {
297 if (!$1.head)
298 $1.head = $1.tail = $2;
299 else
300 grecs_node_bind($1.tail, $2, 0);
301 }
302 $$ = $1;
303 }
304 ;
305
306 elsifcond: elsif DHCPD_EXPR '{' maybe_stmtlist '}'
307 {
308 $$ = grecs_node_create_points(grecs_node_block,
309 @1.beg, @5.end);
310
311 grecs_line_begin();
312 grecs_line_add("elsif", 5);
313
314 $$->ident = grecs_line_finish();
315 $$->idloc = @1;
316 $$->v.value = make_string_value ($2);
317 grecs_node_bind($$, $4, 1);
318 }
319 ;
320
321 opt_elsecond: /* empty */
322 {
323 $$ = NULL;
324 }
325 | elsecond
326 ;
327
328 elsecond: DHCPD_ELSE '{' maybe_stmtlist '}'
329 {
330 $$ = grecs_node_create_points(grecs_node_block,
331 @1.beg, @4.end);
332
333 grecs_line_begin();
334 grecs_line_add("else", 4);
335
336 $$->ident = grecs_line_finish();
337 $$->idloc = @1;
338 $$->v.value = NULL;
339 grecs_node_bind($$, $3, 1);
340 }
341 ;
342
343 %%
344
345 int
346 yyerror(char const *s)
347 {
348 grecs_error(&yylloc, 0, "%s", s);
349 return 0;
350 }
351
352 struct grecs_node *
353 grecs_dhcpd_parser(const char *name, int traceflags)
354 {
355 int rc;
356
357 if (grecs_dhcpd_new_source(name, NULL))
358 return NULL;
359 yy_flex_debug = traceflags & GRECS_TRACE_LEX;
360 yydebug = traceflags & GRECS_TRACE_GRAM;
361 parse_tree = NULL;
362 grecs_line_acc_create();
363 rc = yyparse();
364 grecs_dhcpd_close_sources();
365 if (grecs_error_count)
366 rc = 1;
367 grecs_line_acc_free();
368 if (rc) {
369 grecs_tree_free(parse_tree);
370 parse_tree = NULL;
371 }
372 return parse_tree;
373 }
374
375