dhcp6_parser.yy 16.4 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

  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"
69

70
71
  MAC_SOURCES "mac-sources"
  RELAY_SUPPLIED_OPTIONS "relay-supplied-options"
72

73
74
75
76
77
78
79
  CLIENT_CLASSES "client-classes"
  TEST "test"
  CLIENT_CLASS "client-class"

  RESERVATIONS "reservations"
  DUID "duid"

80
81
82
83
84
85
  LOGGING "Logging"
  LOGGERS "loggers"
  OUTPUT_OPTIONS "output_options"
  OUTPUT "output"
  DEBUGLEVEL "debuglevel"
  SEVERITY "severity"
86
87
88
89
90
91
92
93
94
95
96
97
98
99
;

%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 { }.
100
101
102
// %start map - this will parse everything as generic JSON
// %start dhcp6_map - this will parse everything with Dhcp6 syntax checking
%start syntax_map;
103

104
105
// ---- generic JSON parser ---------------------------------

106
107
108
109
110
111
// 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()); }
112
113
     | map { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
     | list { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
114
115
116
    ;

map: LCURLY_BRACKET {
117
118
119
120
121
122
123
124
125
    // 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.
};
126
127
128
129

// Assignments rule
map_content:  { /* do nothing, it's an empty map */ }
    | STRING COLON value {
130
131
        // map containing a single entry
        ctx.stack_.back()->set($1, $3);
132
    }
133
    | map_content COMMA STRING COLON value {
134
135
        // map consisting of a shorter map followed by comma and string:value
        ctx.stack_.back()->set($3, $5);
136
137
138
    }
    ;

139
140
141
142
143
list: LSQUARE_BRACKET {
    // List parsing about to start
} list_content RSQUARE_BRACKET {
    // list parsing complete. Put any sanity checking here
};
144
145
146

list_content: { /* do nothing, it's an empty list */ }
    | value {
147
148
        // List consisting of a single element.
        ctx.stack_.back()->add($1);
149
    }
150
    | list_content COMMA value {
151
152
        // List ending with , and a value.
        ctx.stack_.back()->add($3);
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
186
187
188
189
190
191
192
193
194
195
// ---- 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();
};

196
197
198
199
global_params: global_param
| global_params COMMA global_param;

// These are the parameters that are allowed in the top-level for
200
// Dhcp6.
201
202
203
204
205
206
207
208
global_param
: preferred_lifetime
| valid_lifetime
| renew_timer
| rebind_timer
| subnet6_list
| interfaces_config
| lease_database
209
210
| mac_sources
| relay_supplied_options
211
212
| client_classes
| option_data_list
213
214
215
;

preferred_lifetime: PREFERRED_LIFETIME COLON INTEGER {
216
217
    ElementPtr prf(new IntElement($3));
    ctx.stack_.back()->set("preferred-lifetime", prf);
218
219
220
};

valid_lifetime: VALID_LIFETIME COLON INTEGER {
221
222
    ElementPtr prf(new IntElement($3));
    ctx.stack_.back()->set("valid-lifetime", prf);
223
224
225
};

renew_timer: RENEW_TIMER COLON INTEGER {
226
227
    ElementPtr prf(new IntElement($3));
    ctx.stack_.back()->set("renew-timer", prf);
228
229
};

230
231
232
233
rebind_timer: REBIND_TIMER COLON INTEGER {
    ElementPtr prf(new IntElement($3));
    ctx.stack_.back()->set("rebind-timer", prf);
};
234

235
236
237
238
239
240
241
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();
};
242

243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
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;
259

260
261
lease_database_map_params: lease_database_map_param
| lease_database_map_params COMMA lease_database_map_param;
262

263
lease_database_map_param: lease_database_type;
264

265
266
267
lease_database_type: TYPE COLON STRING {
    ElementPtr prf(new StringElement($3));
    ctx.stack_.back()->set("type", prf);
268
269
};

270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
mac_sources: MAC_SOURCES {
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("mac-sources", l);
    ctx.stack_.push_back(l);
} COLON list {
    ctx.stack_.pop_back();
};

relay_supplied_options: RELAY_SUPPLIED_OPTIONS {
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("relay-supplied-options", l);
    ctx.stack_.push_back(l);
} COLON list {
    ctx.stack_.pop_back();
};

286
287
288
289
// This defines subnet6 as a list of maps.
// "subnet6": [ ... ]
subnet6_list: SUBNET6 COLON LSQUARE_BRACKET {
    ElementPtr l(new ListElement());
290
    ctx.stack_.back()->set("subnet6", l);
291
292
    ctx.stack_.push_back(l);
} subnet6_list_content RSQUARE_BRACKET {
293
    ctx.stack_.pop_back();
294
295
296
297
298
299
300
301
302
303
};

// 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
;

304
305
// --- Subnet definitions -------------------------------

306
307
// This defines a single subnet, i.e. a single map with
// subnet6 array.
308
309
310
311
312
313
314
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;
315

316
// This defines that subnet can have one or more parameters.
317
subnet6_params: subnet6_param
318
319
| subnet6_params COMMA subnet6_param;

320
321
// This defines a list of allowed parameters for each subnet.
subnet6_param: option_data_list
322
| pools_list
323
324
325
326
| subnet
| interface
| client_class
| reservations
327
328
;

329
330
331
332
333
334
335
336
337
338
339
340
341
subnet: SUBNET COLON STRING {
    ElementPtr subnet(new StringElement($3)); ctx.stack_.back()->set("subnet", subnet);
};

interface: INTERFACE COLON STRING {
    ElementPtr iface(new StringElement($3)); ctx.stack_.back()->set("interface", iface);
};

subnet: CLIENT_CLASS COLON STRING {
    ElementPtr cls(new StringElement($3)); ctx.stack_.back()->set("client-class", cls);
};


342
343
344
345
// ---- option-data --------------------------

// This defines the "option-data": [ ... ] entry that may appear
// in several places, but most notably in subnet6 entries.
346
347
348
349
350
351
352
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();
};
353
354
355
356
357
358
359
360
361

// 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.
362
363
364
365
366
367
368
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();
};
369
370
371
372
373
374
375

// 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;

376
377
378
379
380
381
382
383
384
385
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);
};
386
387
388
389

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

// This defines the "pools": [ ... ] entry that may appear in subnet6.
390
391
392
393
394
395
396
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();
};
397
398
399
400
401
402
403

// 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;

404
405
406
407
408
409
410
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();
};
411
412
413
414

pool_params: pool_param
| pool_params COMMA pool_param;

415
pool_param: POOL COLON STRING {
416
    ElementPtr pool(new StringElement($3)); ctx.stack_.back()->set("pool", pool);
417
}
418
419
| option_data_list;

420
421
// --- end of pools definition -------------------------------

422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
// --- reservations ------------------------------------------
reservations: RESERVATIONS COLON LSQUARE_BRACKET {
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("reservations", l);
    ctx.stack_.push_back(l);
} reservations_list {
    ctx.stack_.pop_back();
} RSQUARE_BRACKET;

reservations_list: { }
| reservation
| reservations_list COMMA reservation;

reservation: LCURLY_BRACKET {
    ElementPtr m(new MapElement());
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} reservation_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};

reservation_params: reservation_param
| reservation_params COMMA reservation_param;

// @todo probably need to add mac-address as well here
reservation_param:
| duid
| reservation_client_classes
;

duid: DUID COLON STRING {
    ElementPtr d(new StringElement($3)); ctx.stack_.back()->set("duid", d);
};

reservation_client_classes: CLIENT_CLASSES COLON {
    ElementPtr c(new ListElement());
    ctx.stack_.back()->set("client-classes", c);
    ctx.stack_.push_back(c);
} list {
    ctx.stack_.pop_back();
  };

// --- end of reservations definitions -----------------------

// --- client classes ----------------------------------------
client_classes: CLIENT_CLASSES COLON LSQUARE_BRACKET {
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("client-classes", l);
    ctx.stack_.push_back(l);
} client_classes_list RSQUARE_BRACKET {
    ctx.stack_.pop_back();
};

client_classes_list: client_class
| client_classes_list COMMA client_class;

client_class: LCURLY_BRACKET {
    ElementPtr m(new MapElement());
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} client_class_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};

client_class_params: client_class_param
| client_class_params COMMA client_class_param;

client_class_param:
| client_class_name
| client_class_test
| option_data_list
;

client_class_name: NAME COLON STRING {
    ElementPtr name(new StringElement($3));
    ctx.stack_.back()->set("name", name);
};

client_class_test: TEST COLON STRING {
    ElementPtr test(new StringElement($3));
    ctx.stack_.back()->set("test", test);
}


// --- end of client classes ---------------------------------

508
509
510
511
512
513
// --- 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 {
514
515
516
    ElementPtr m(new MapElement());
    ctx.stack_.back()->set("Logging", m);
    ctx.stack_.push_back(m);
517
} logging_params RCURLY_BRACKET {
518
    ctx.stack_.pop_back();
519
520
521
522
523
524
525
526
527
528
529
530
531
};

// 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": [ ... ].
532
533
534
535
536
537
538
loggers: LOGGERS COLON {
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("loggers", l);
    ctx.stack_.push_back(l);
} LSQUARE_BRACKET loggers_entries RSQUARE_BRACKET {
    ctx.stack_.pop_back();
};
539
540
541
542
543
544
545

// 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.
546
547
548
549
550
551
552
logger_entry: LCURLY_BRACKET {
    ElementPtr l(new MapElement());
    ctx.stack_.back()->add(l);
    ctx.stack_.push_back(l);
} logger_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};
553
554
555
556

logger_params: logger_param
| logger_params COMMA logger_param;

557
logger_param: logger_name
558
559
560
561
562
| output_options_list
| debuglevel
| severity
;

563
564
565
566
567
568
569
570
571
572
logger_name: NAME COLON STRING {
    ElementPtr name(new StringElement($3)); ctx.stack_.back()->set("name", name);
};

debuglevel: DEBUGLEVEL COLON INTEGER {
    ElementPtr dl(new IntElement($3)); ctx.stack_.back()->set("debuglevel", dl);
};
severity: SEVERITY COLON STRING {
    ElementPtr sev(new StringElement($3)); ctx.stack_.back()->set("severity", sev);
};
573

574
575
576
577
578
579
580
output_options_list: OUTPUT_OPTIONS COLON {
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("output_options", l);
    ctx.stack_.push_back(l);
} LSQUARE_BRACKET output_options_list_content RSQUARE_BRACKET {
    ctx.stack_.pop_back();
};
581
582
583
584

output_options_list_content: output_entry
| output_options_list_content COMMA output_entry;

585
586
587
588
589
590
591
output_entry: LCURLY_BRACKET {
    ElementPtr m(new MapElement());
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} output_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};
592
593
594
595

output_params: output_param
| output_params COMMA output_param;

596
597
598
output_param: OUTPUT COLON STRING {
    ElementPtr sev(new StringElement($3)); ctx.stack_.back()->set("output", sev);
};
599
600
601



602
603
604
605
%%

void
isc::dhcp::Dhcp6Parser::error(const location_type& loc,
606
                              const std::string& what)
607
608
609
{
    ctx.error(loc, what);
}