dhcp6_parser.yy 12.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/* Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")

   This Source Code Form is subject to the terms of the Mozilla Public
   License, v. 2.0. If a copy of the MPL was not distributed with this
   file, You can obtain one at http://mozilla.org/MPL/2.0/. */

%skeleton "lalr1.cc" /* -*- C++ -*- */
%require "3.0.0"
%defines
%define parser_class_name {Dhcp6Parser}
11
%define api.prefix {parser6_}
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
%define api.token.constructor
%define api.value.type variant
%define api.namespace {isc::dhcp}
%define parse.assert
%code requires
{
#include <string>
#include <cc/data.h>
#include <dhcp/option.h>
#include <boost/lexical_cast.hpp>
#include <dhcp6/parser_context_decl.h>

using namespace isc::dhcp;
using namespace isc::data;
using namespace std;
}
// The parsing context.
%param { isc::dhcp::Parser6Context& ctx }
%locations
%define parse.trace
%define parse.error verbose
%code
{
#include <dhcp6/parser_context.h>

}

%define api.token.prefix {TOKEN_}
// Tokens in an order which makes sense and related to the intented use.
%token
  END  0  "end of file"
  COMMA ","
  COLON ":"
  LSQUARE_BRACKET "["
  RSQUARE_BRACKET "]"
  LCURLY_BRACKET "{"
  RCURLY_BRACKET "}"
  NULL_TYPE "null"
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

  DHCP6 "Dhcp6"
  INTERFACES_CONFIG "interfaces-config"
  INTERFACES "interfaces"
  LEASE_DATABASE "lease-database"
  TYPE "type"
  PREFERRED_LIFETIME "preferred-lifetime"
  VALID_LIFETIME "valid-lifetime"
  RENEW_TIMER "renew-timer"
  REBIND_TIMER "rebind-timer"
  SUBNET6 "subnet6"
  OPTION_DATA "option-data"
  NAME "name"
  DATA "data"
  CODE "code"
  POOLS "pools"
  POOL "pool"
  SUBNET "subnet"
  INTERFACE "interface"

  LOGGING "Logging"
  LOGGERS "loggers"
  OUTPUT_OPTIONS "output_options"
  OUTPUT "output"
  DEBUGLEVEL "debuglevel"
  SEVERITY "severity"
76
77
78
79
80
81
82
83
84
85
86
87
88
89
;

%token <std::string> STRING "constant string"
%token <int64_t> INTEGER "integer"
%token <double> FLOAT "floating point"
%token <bool> BOOLEAN "boolean"

%type <ElementPtr> value

%printer { yyoutput << $$; } <*>;

%%
// The whole grammar starts with a map, because the config file
// constists of Dhcp, Logger and DhcpDdns entries in one big { }.
90
91
92
// %start map - this will parse everything as generic JSON
// %start dhcp6_map - this will parse everything with Dhcp6 syntax checking
%start syntax_map;
93

94
95
// ---- generic JSON parser ---------------------------------

96
97
98
99
100
101
// Values rule
value : INTEGER { $$ = ElementPtr(new IntElement($1)); }
     | FLOAT { $$ = ElementPtr(new DoubleElement($1)); }
     | BOOLEAN { $$ = ElementPtr(new BoolElement($1)); }
     | STRING { $$ = ElementPtr(new StringElement($1)); }
     | NULL_TYPE { $$ = ElementPtr(new NullElement()); }
102
103
     | map { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
     | list { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
104
105
106
    ;

map: LCURLY_BRACKET {
107
108
109
110
111
112
113
114
115
    // This code is executed when we're about to start parsing
    // the content of the map
    ElementPtr m(new MapElement());
    ctx.stack_.push_back(m);
} map_content RCURLY_BRACKET {
    // map parsing completed. If we ever want to do any wrap up
    // (maybe some sanity checking), this would be the best place
    // for it.
};
116
117
118
119

// Assignments rule
map_content:  { /* do nothing, it's an empty map */ }
    | STRING COLON value {
120
121
        // map containing a single entry
        ctx.stack_.back()->set($1, $3);
122
    }
123
    | map_content COMMA STRING COLON value {
124
125
        // map consisting of a shorter map followed by comma and string:value
        ctx.stack_.back()->set($3, $5);
126
127
128
    }
    ;

129
130
131
132
133
list: LSQUARE_BRACKET {
    // List parsing about to start
} list_content RSQUARE_BRACKET {
    // list parsing complete. Put any sanity checking here
};
134
135
136

list_content: { /* do nothing, it's an empty list */ }
    | value {
137
138
        // List consisting of a single element.
        ctx.stack_.back()->add($1);
139
    }
140
    | list_content COMMA value {
141
142
        // List ending with , and a value.
        ctx.stack_.back()->add($3);
143
144
145
    }
    ;

146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// ---- generic JSON parser ends here ----------------------------------

// ---- syntax checking parser starts here -----------------------------

// This defines the top-level { } that holds Dhcp6, Dhcp4, DhcpDdns or Logging
// objects.
syntax_map: LCURLY_BRACKET {
    // This code is executed when we're about to start parsing
    // the content of the map
    ElementPtr m(new MapElement());
    ctx.stack_.push_back(m);
} global_objects RCURLY_BRACKET {
    // map parsing completed. If we ever want to do any wrap up
    // (maybe some sanity checking), this would be the best place
    // for it.
};

// This represents a single top level entry, e.g. Dhcp6 or DhcpDdns.
global_object: dhcp6_object
| logging_object;

// This represents top-level entries: Dhcp6, Dhcp4, DhcpDdns, Logging
global_objects
: global_object
| global_objects COMMA global_object
;

dhcp6_object: DHCP6 COLON LCURLY_BRACKET {
    // This code is executed when we're about to start parsing
    // the content of the map
    ElementPtr m(new MapElement());
    ctx.stack_.back()->set("Dhcp6", m);
    ctx.stack_.push_back(m);
} global_params RCURLY_BRACKET {
    // map parsing completed. If we ever want to do any wrap up
    // (maybe some sanity checking), this would be the best place
    // for it.
    ctx.stack_.pop_back();
};

186
187
188
189
global_params: global_param
| global_params COMMA global_param;

// These are the parameters that are allowed in the top-level for
190
// Dhcp6.
191
192
193
194
195
196
197
198
199
200
201
global_param
: preferred_lifetime
| valid_lifetime
| renew_timer
| rebind_timer
| subnet6_list
| interfaces_config
| lease_database
;

preferred_lifetime: PREFERRED_LIFETIME COLON INTEGER {
202
203
    ElementPtr prf(new IntElement($3));
    ctx.stack_.back()->set("preferred-lifetime", prf);
204
205
206
};

valid_lifetime: VALID_LIFETIME COLON INTEGER {
207
208
    ElementPtr prf(new IntElement($3));
    ctx.stack_.back()->set("valid-lifetime", prf);
209
210
211
};

renew_timer: RENEW_TIMER COLON INTEGER {
212
213
    ElementPtr prf(new IntElement($3));
    ctx.stack_.back()->set("renew-timer", prf);
214
215
};

216
217
218
219
rebind_timer: REBIND_TIMER COLON INTEGER {
    ElementPtr prf(new IntElement($3));
    ctx.stack_.back()->set("rebind-timer", prf);
};
220

221
222
223
224
225
226
227
interfaces_config: INTERFACES_CONFIG COLON {
    ElementPtr i(new MapElement());
    ctx.stack_.back()->set("interfaces-config", i);
    ctx.stack_.push_back(i);
 } LCURLY_BRACKET interface_config_map RCURLY_BRACKET {
    ctx.stack_.pop_back();
};
228

229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
interface_config_map: INTERFACES {
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("interfaces", l);
    ctx.stack_.push_back(l);
 } COLON list {
     ctx.stack_.pop_back();
 }

lease_database: LEASE_DATABASE {
    ElementPtr i(new MapElement());
    ctx.stack_.back()->set("lease-database", i);
    ctx.stack_.push_back(i);
}
COLON LCURLY_BRACKET lease_database_map_params {
     ctx.stack_.pop_back();
} RCURLY_BRACKET;
245

246
247
lease_database_map_params: lease_database_map_param
| lease_database_map_params COMMA lease_database_map_param;
248

249
lease_database_map_param: lease_database_type;
250

251
252
253
lease_database_type: TYPE COLON STRING {
    ElementPtr prf(new StringElement($3));
    ctx.stack_.back()->set("type", prf);
254
255
256
257
258
259
};

// This defines subnet6 as a list of maps.
// "subnet6": [ ... ]
subnet6_list: SUBNET6 COLON LSQUARE_BRACKET {
    ElementPtr l(new ListElement());
260
    ctx.stack_.back()->set("subnet6", l);
261
262
    ctx.stack_.push_back(l);
} subnet6_list_content RSQUARE_BRACKET {
263
    ctx.stack_.pop_back();
264
265
266
267
268
269
270
271
272
273
274
275
};

// This defines the ... in "subnet6": [ ... ]
// It can either be empty (no subnets defined), have one subnet
// or have multiple subnets separate by comma.
subnet6_list_content: { /* no subnets defined at all */ }
| subnet6
| subnet6_list_content COMMA subnet6
;

// This defines a single subnet, i.e. a single map with
// subnet6 array.
276
277
278
279
280
281
282
subnet6: LCURLY_BRACKET {
    ElementPtr m(new MapElement());
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} subnet6_params {
    ctx.stack_.pop_back();
} RCURLY_BRACKET;
283

284
subnet6_params: subnet6_param
285
286
287
288
289
| subnet6_params COMMA subnet6_param;

subnet6_param: { /* empty list */ }
| option_data_list
| pools_list
290
291
292
293
294
295
296
| SUBNET COLON STRING {
    ElementPtr name(new StringElement($3)); ctx.stack_.back()->set("subnet", name);

 }
| INTERFACE COLON STRING {
    ElementPtr name(new StringElement($3)); ctx.stack_.back()->set("interface", name);
 }
297
298
299
300
301
302
;

// ---- option-data --------------------------

// This defines the "option-data": [ ... ] entry that may appear
// in several places, but most notably in subnet6 entries.
303
304
305
306
307
308
309
option_data_list: OPTION_DATA {
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("option-data", l);
    ctx.stack_.push_back(l);
} COLON LSQUARE_BRACKET option_data_list_content RSQUARE_BRACKET {
    ctx.stack_.pop_back();
};
310
311
312
313
314
315
316
317
318

// This defines the content of option-data. It may be empty,
// have one entry or multiple entries separated by comma.
option_data_list_content: { }
| option_data_entry
| option_data_list_content COMMA option_data_entry;

// This defines th content of a single entry { ... } within
// option-data list.
319
320
321
322
323
324
325
option_data_entry: LCURLY_BRACKET {
    ElementPtr m(new MapElement());
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} option_data_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};
326
327
328
329
330
331
332

// This defines parameters specified inside the map that itself
// is an entry in option-data list.
option_data_params: {}
| option_data_param
| option_data_params COMMA option_data_param;

333
334
335
336
337
338
339
340
341
342
option_data_param:
NAME COLON STRING {
    ElementPtr name(new StringElement($3)); ctx.stack_.back()->set("name", name);
}
| DATA COLON STRING {
    ElementPtr data(new StringElement($3)); ctx.stack_.back()->set("data", data);
}
| CODE COLON INTEGER {
    ElementPtr code(new IntElement($3)); ctx.stack_.back()->set("code", code);
};
343
344
345
346

// ---- pools ------------------------------------

// This defines the "pools": [ ... ] entry that may appear in subnet6.
347
348
349
350
351
352
353
pools_list: POOLS COLON {
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("pools", l);
    ctx.stack_.push_back(l);
} LSQUARE_BRACKET pools_list_content RSQUARE_BRACKET {
    ctx.stack_.pop_back();
};
354
355
356
357
358
359
360

// Pools may be empty, contain a single pool entry or multiple entries
// separate by commas.
pools_list_content: { }
| pool_entry
| pools_list_content COMMA pool_entry;

361
362
363
364
365
366
367
pool_entry: LCURLY_BRACKET {
    ElementPtr m(new MapElement());
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} pool_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};
368
369
370
371

pool_params: pool_param
| pool_params COMMA pool_param;

372
373
374
pool_param: POOL COLON STRING {
    ElementPtr name(new StringElement($3)); ctx.stack_.back()->set("pool", name);
}
375
376
| option_data_list;

377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
// --- end of pools definition -------------------------------

// --- logging entry -----------------------------------------

// This defines the top level "Logging" object. It parses
// the following "Logging": { ... }. The ... is defined
// by logging_params
logging_object: LOGGING COLON LCURLY_BRACKET {

} logging_params RCURLY_BRACKET {

};

// This defines the list of allowed parameters that may appear
// in the top-level Logging object. It can either be a single
// parameter or several parameters separated by commas.
logging_params: logging_param
| logging_params COMMA logging_param;

// There's currently only one parameter defined, which is "loggers".
logging_param: loggers;

// "loggers", the only parameter currently defined in "Logging" object,
// is "Loggers": [ ... ].
loggers: LOGGERS COLON LSQUARE_BRACKET loggers_entries RSQUARE_BRACKET;

// These are the parameters allowed in loggers: either one logger
// entry or multiple entries separate by commas.
loggers_entries: logger_entry
| loggers_entries COMMA logger_entry;

// This defines a single entry defined in loggers in Logging.
logger_entry: LCURLY_BRACKET logger_params RCURLY_BRACKET;

logger_params: logger_param
| logger_params COMMA logger_param;

logger_param: NAME COLON STRING
| output_options_list
| debuglevel
| severity
;

debuglevel: DEBUGLEVEL COLON INTEGER;
severity: SEVERITY COLON STRING;

output_options_list: OUTPUT_OPTIONS COLON LSQUARE_BRACKET output_options_list_content RSQUARE_BRACKET;

output_options_list_content: output_entry
| output_options_list_content COMMA output_entry;

output_entry: LCURLY_BRACKET output_params RCURLY_BRACKET;

output_params: output_param
| output_params COMMA output_param;

output_param: OUTPUT COLON STRING;
434
435
436
437




438
439
440
441
%%

void
isc::dhcp::Dhcp6Parser::error(const location_type& loc,
442
                              const std::string& what)
443
444
445
{
    ctx.error(loc, what);
}