dhcp6_parser.yy 35.7 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
%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>

}

39

40
41
42
43
44
45
46
47
48
49
50
%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"
51
52
53
54

  DHCP6 "Dhcp6"
  INTERFACES_CONFIG "interfaces-config"
  INTERFACES "interfaces"
55

56
  LEASE_DATABASE "lease-database"
57
  HOSTS_DATABASE "hosts-database"
58
  TYPE "type"
59
60
61
  USER "user"
  PASSWORD "password"
  HOST "host"
62
  PERSIST "persist"
63
  LFC_INTERVAL "lfc-interval"
64

65
66
67
68
69
70
71
72
73
  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"
74
  SPACE "space"
75
  CSV_FORMAT "csv-format"
76

77
78
  POOLS "pools"
  POOL "pool"
79
80
81
82
83
  PD_POOLS "pd-pools"
  PREFIX "prefix"
  PREFIX_LEN "prefix-len"
  DELEGATED_LEN "delegated-len"

84
85
  SUBNET "subnet"
  INTERFACE "interface"
86
  ID "id"
87

88
89
  MAC_SOURCES "mac-sources"
  RELAY_SUPPLIED_OPTIONS "relay-supplied-options"
90
  HOST_RESERVATION_IDENTIFIERS "host-reservation-identifiers"
91

92
93
94
95
96
  CLIENT_CLASSES "client-classes"
  TEST "test"
  CLIENT_CLASS "client-class"

  RESERVATIONS "reservations"
97
98
  IP_ADDRESSES "ip-addresses"
  PREFIXES "prefixes"
99
  DUID "duid"
100
101
  HW_ADDRESS "hw-address"
  HOSTNAME "hostname"
102

103
104
105
106
107
108
109
110
111
  HOOKS_LIBRARIES "hooks-libraries"
  LIBRARY "library"

  EXPIRED_LEASES_PROCESSING "expired-leases-processing"

  SERVER_ID "server-id"
  IDENTIFIER "identifier"
  HTYPE "htype"
  TIME "time"
112
  ENTERPRISE_ID "enterprise-id"
113

114
115
  DHCP4O6_PORT "dhcp4o6-port"

116
117
118
119
120
121
  LOGGING "Logging"
  LOGGERS "loggers"
  OUTPUT_OPTIONS "output_options"
  OUTPUT "output"
  DEBUGLEVEL "debuglevel"
  SEVERITY "severity"
122

123
124
125
126
  DHCP_DDNS "dhcp-ddns"
  ENABLE_UPDATES "enable-updates"
  QUALIFYING_SUFFIX "qualifying-suffix"

127
128
129
  DHCP4 "Dhcp4"
  DHCPDDNS "DhcpDdns"

130
131
132
 // Not real tokens, just a way to signal what the parser is expected to
 // parse.
  TOPLEVEL_GENERIC_JSON
133
  TOPLEVEL_DHCP6
134
135
136
137
138
139
140
  SUB_INTERFACES6
  SUB_SUBNET6
  SUB_POOL6
  SUB_PD_POOL
  SUB_RESERVATION
  SUB_OPTION_DATA
  SUB_HOOKS_LIBRARY
141
  SUB_JSON
142
143
144
145
146
147
148
149
150
151
152
153
;

%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 << $$; } <*>;

%%
154

155
156
// The whole grammar starts with a map, because the config file
// constists of Dhcp, Logger and DhcpDdns entries in one big { }.
157
// We made the same for subparsers at the exception of the JSON value.
158
159
%start start;

160
161
start: TOPLEVEL_GENERIC_JSON { ctx.ctx_ = ctx.NO_KEYWORD; } map2
     | TOPLEVEL_DHCP6 { ctx.ctx_ = ctx.CONFIG; } syntax_map
162
163
164
165
166
167
168
     | SUB_INTERFACES6 { ctx.ctx_ = ctx.INTERFACES_CONFIG; } sub_interfaces6
     | SUB_SUBNET6 { ctx.ctx_ = ctx.SUBNET6; } sub_subnet6
     | SUB_POOL6 { ctx.ctx_ = ctx.POOLS; } sub_pool6
     | SUB_PD_POOL { ctx.ctx_ = ctx.PD_POOLS; } sub_pd_pool
     | SUB_RESERVATION { ctx.ctx_ = ctx.RESERVATIONS; } sub_reservation
     | SUB_OPTION_DATA { ctx.ctx_ = ctx.OPTION_DATA; } sub_option_data
     | SUB_HOOKS_LIBRARY { ctx.ctx_ = ctx.HOOKS_LIBRARIES; } sub_hooks_library
169
     | SUB_JSON { ctx.ctx_ = ctx.NO_KEYWORD; } sub_json
170
     ;
171

172
173
// ---- generic JSON parser ---------------------------------

174
175
// Note that ctx_ is NO_KEYWORD here

176
// Values rule
177
178
179
180
181
value: INTEGER { $$ = ElementPtr(new IntElement($1, ctx.loc2pos(@1))); }
     | FLOAT { $$ = ElementPtr(new DoubleElement($1, ctx.loc2pos(@1))); }
     | BOOLEAN { $$ = ElementPtr(new BoolElement($1, ctx.loc2pos(@1))); }
     | STRING { $$ = ElementPtr(new StringElement($1, ctx.loc2pos(@1))); }
     | NULL_TYPE { $$ = ElementPtr(new NullElement(ctx.loc2pos(@1))); }
182
183
184
     | map2 { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
     | list_generic { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
     ;
185

186
187
188
189
190
sub_json: value {
    // Push back the JSON value on the stack
    ctx.stack_.push_back($1);
};

191
map2: LCURLY_BRACKET {
192
193
    // This code is executed when we're about to start parsing
    // the content of the map
194
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
195
196
197
198
199
200
    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.
};
201
202

// Assignments rule
203
map_content: %empty // empty map
204
           | not_empty_map
205
           ;
206

207
208
209
210
211
212
213
214
215
216
217
not_empty_map: STRING COLON value {
                  // map containing a single entry
                  ctx.stack_.back()->set($1, $3);
                  }
             | not_empty_map COMMA STRING COLON value {
                  // map consisting of a shorter map followed by
                  // comma and string:value
                  ctx.stack_.back()->set($3, $5);
                  }
             ;

218
list_generic: LSQUARE_BRACKET {
219
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
220
    ctx.stack_.push_back(l);
221
222
223
} list_content RSQUARE_BRACKET {
    // list parsing complete. Put any sanity checking here
};
224
225
226

// This one is used in syntax parser.
list2: LSQUARE_BRACKET {
227
228
229
    // List parsing about to start
} list_content RSQUARE_BRACKET {
    // list parsing complete. Put any sanity checking here
230
    //ctx.stack_.pop_back();
231
};
232

233
list_content: %empty // Empty list
234
            | not_empty_list
235
            ;
236

237
238
239
240
241
242
243
244
245
246
not_empty_list: value {
                  // List consisting of a single element.
                  ctx.stack_.back()->add($1);
                  }
              | not_empty_list COMMA value {
                  // List ending with , and a value.
                  ctx.stack_.back()->add($3);
                  }
              ;

247
248
249
250
// ---- generic JSON parser ends here ----------------------------------

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

251
252
253
254
255
256
257
258
259
// Unknown keyword in a map
unknown_map_entry: STRING COLON {
    const std::string& where = ctx.context_name();
    const std::string& keyword = $1;
    error(@1,
          "got unexpected keyword \"" + keyword + "\" in " + where + " map.");
}


260
261
// This defines the top-level { } that holds Dhcp6, Dhcp4, DhcpDdns or Logging
// objects.
262
// ctx_ = CONFIG
263
264
265
syntax_map: LCURLY_BRACKET {
    // This code is executed when we're about to start parsing
    // the content of the map
266
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
267
268
269
270
271
272
273
274
    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 top-level entries: Dhcp6, Dhcp4, DhcpDdns, Logging
275
276
277
global_objects: global_object
              | global_objects COMMA global_object
              ;
278

279
280
281
// This represents a single top level entry, e.g. Dhcp6 or DhcpDdns.
global_object: dhcp6_object
             | logging_object
282
283
284
             | dhcp4_json_object
             | dhcpddns_json_object
             | unknown_map_entry
285
286
             ;

287
dhcp6_object: DHCP6 {
288
289
    // This code is executed when we're about to start parsing
    // the content of the map
290
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
291
292
    ctx.stack_.back()->set("Dhcp6", m);
    ctx.stack_.push_back(m);
293
294
    ctx.enter(ctx.DHCP6);
} COLON LCURLY_BRACKET global_params RCURLY_BRACKET {
295
296
297
298
    // 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();
299
    ctx.leave();
300
301
};

302
global_params: global_param
303
304
             | global_params COMMA global_param
             ;
305
306

// These are the parameters that are allowed in the top-level for
307
// Dhcp6.
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
global_param: preferred_lifetime
            | valid_lifetime
            | renew_timer
            | rebind_timer
            | subnet6_list
            | interfaces_config
            | lease_database
            | hosts_database
            | mac_sources
            | relay_supplied_options
            | host_reservation_identifiers
            | client_classes
            | option_data_list
            | hooks_libraries
            | expired_leases_processing
            | server_id
            | dhcp4o6_port
            | dhcp_ddns
326
            | unknown_map_entry
327
            ;
328
329

preferred_lifetime: PREFERRED_LIFETIME COLON INTEGER {
330
    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
331
    ctx.stack_.back()->set("preferred-lifetime", prf);
332
333
334
};

valid_lifetime: VALID_LIFETIME COLON INTEGER {
335
    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
336
    ctx.stack_.back()->set("valid-lifetime", prf);
337
338
339
};

renew_timer: RENEW_TIMER COLON INTEGER {
340
    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
341
    ctx.stack_.back()->set("renew-timer", prf);
342
343
};

344
rebind_timer: REBIND_TIMER COLON INTEGER {
345
    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
346
347
    ctx.stack_.back()->set("rebind-timer", prf);
};
348

349
interfaces_config: INTERFACES_CONFIG {
350
    ElementPtr i(new MapElement(ctx.loc2pos(@1)));
351
352
    ctx.stack_.back()->set("interfaces-config", i);
    ctx.stack_.push_back(i);
353
354
    ctx.enter(ctx.INTERFACES_CONFIG);
} COLON LCURLY_BRACKET interface_config_map RCURLY_BRACKET {
355
    ctx.stack_.pop_back();
356
    ctx.leave();
357
};
358

359
360
// subparser: similar to the corresponding rule but without parent
// so the stack is empty at the rule entry.
361
362
363
364
365
366
367
368
sub_interfaces6: LCURLY_BRACKET {
    // Parse the interfaces-config map
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
    ctx.stack_.push_back(m);
} interface_config_map RCURLY_BRACKET {
    // parsing completed
};

369
interface_config_map: INTERFACES {
370
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
371
372
    ctx.stack_.back()->set("interfaces", l);
    ctx.stack_.push_back(l);
373
374
375
376
377
    ctx.enter(ctx.NO_KEYWORD);
} COLON list2 {
    ctx.stack_.pop_back();
    ctx.leave();
};
378
379

lease_database: LEASE_DATABASE {
380
    ElementPtr i(new MapElement(ctx.loc2pos(@1)));
381
382
    ctx.stack_.back()->set("lease-database", i);
    ctx.stack_.push_back(i);
383
    ctx.enter(ctx.LEASE_DATABASE);
384
385
386
387
} COLON LCURLY_BRACKET database_map_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
    ctx.leave();
};
388

389
hosts_database: HOSTS_DATABASE {
390
    ElementPtr i(new MapElement(ctx.loc2pos(@1)));
391
392
    ctx.stack_.back()->set("hosts-database", i);
    ctx.stack_.push_back(i);
393
    ctx.enter(ctx.HOSTS_DATABASE);
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
} COLON LCURLY_BRACKET database_map_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
    ctx.leave();
};

database_map_params: database_map_param
                   | database_map_params COMMA database_map_param
                   ;

database_map_param: type
                  | user
                  | password
                  | host
                  | name
                  | persist
409
410
                  | lfc_interval
                  | unknown_map_entry
411
;
412

413
414
415
type: TYPE {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
416
    ElementPtr prf(new StringElement($4, ctx.loc2pos(@4)));
417
    ctx.stack_.back()->set("type", prf);
418
    ctx.leave();
419
420
};

421
422
423
user: USER {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
424
    ElementPtr user(new StringElement($4, ctx.loc2pos(@4)));
425
    ctx.stack_.back()->set("user", user);
426
    ctx.leave();
427
428
};

429
430
431
password: PASSWORD {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
432
    ElementPtr pwd(new StringElement($4, ctx.loc2pos(@4)));
433
    ctx.stack_.back()->set("password", pwd);
434
    ctx.leave();
435
436
};

437
438
439
host: HOST {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
440
    ElementPtr h(new StringElement($4, ctx.loc2pos(@4)));
441
    ctx.stack_.back()->set("host", h);
442
    ctx.leave();
443
444
};

445
446
447
name: NAME {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
448
    ElementPtr name(new StringElement($4, ctx.loc2pos(@4)));
449
    ctx.stack_.back()->set("name", name);
450
    ctx.leave();
451
452
};

453
persist: PERSIST COLON BOOLEAN {
454
    ElementPtr n(new BoolElement($3, ctx.loc2pos(@3)));
455
456
457
    ctx.stack_.back()->set("persist", n);
};

458
lfc_interval: LFC_INTERVAL COLON INTEGER {
459
    ElementPtr n(new IntElement($3, ctx.loc2pos(@3)));
460
461
462
    ctx.stack_.back()->set("lfc-interval", n);
};

463
mac_sources: MAC_SOURCES {
464
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
465
466
    ctx.stack_.back()->set("mac-sources", l);
    ctx.stack_.push_back(l);
467
    ctx.enter(ctx.MAC_SOURCES);
468
} COLON LSQUARE_BRACKET mac_sources_list RSQUARE_BRACKET {
469
    ctx.stack_.pop_back();
470
    ctx.leave();
471
472
};

473
mac_sources_list: mac_sources_value
474
475
                | mac_sources_list COMMA mac_sources_value
;
476

477
478
mac_sources_value: duid_id
                 | string_id
479
480
                 ;

481
duid_id : DUID {
482
    ElementPtr duid(new StringElement("duid", ctx.loc2pos(@1)));
483
484
485
486
    ctx.stack_.back()->add(duid);
};

string_id : STRING {
487
    ElementPtr duid(new StringElement($1, ctx.loc2pos(@1)));
488
489
490
    ctx.stack_.back()->add(duid);
};

491
host_reservation_identifiers: HOST_RESERVATION_IDENTIFIERS {
492
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
493
494
    ctx.stack_.back()->set("host-reservation-identifiers", l);
    ctx.stack_.push_back(l);
495
496
    ctx.enter(ctx.HOST_RESERVATION_IDENTIFIERS);    
} COLON LSQUARE_BRACKET host_reservation_identifiers_list RSQUARE_BRACKET {
497
    ctx.stack_.pop_back();
498
    ctx.leave();
499
500
501
};

host_reservation_identifiers_list: host_reservation_identifier
502
503
    | host_reservation_identifiers_list COMMA host_reservation_identifier
    ;
504

505
506
host_reservation_identifier: duid_id
                           | hw_address_id
507
                           ;
508

509
hw_address_id : HW_ADDRESS {
510
    ElementPtr hwaddr(new StringElement("hw-address", ctx.loc2pos(@1)));
511
512
513
    ctx.stack_.back()->add(hwaddr);
};

514
relay_supplied_options: RELAY_SUPPLIED_OPTIONS {
515
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
516
517
    ctx.stack_.back()->set("relay-supplied-options", l);
    ctx.stack_.push_back(l);
518
    ctx.enter(ctx.NO_KEYWORD);
519
} COLON list2 {
520
    ctx.stack_.pop_back();
521
    ctx.leave();
522
523
};

524
hooks_libraries: HOOKS_LIBRARIES {
525
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
526
527
    ctx.stack_.back()->set("hooks-libraries", l);
    ctx.stack_.push_back(l);
528
529
    ctx.enter(ctx.HOOKS_LIBRARIES);
} COLON LSQUARE_BRACKET hooks_libraries_list RSQUARE_BRACKET {
530
    ctx.stack_.pop_back();
531
    ctx.leave();
532
533
};

534
hooks_libraries_list: %empty
535
                    | not_empty_hooks_libraries_list
536
                    ;
537

538
539
540
541
not_empty_hooks_libraries_list: hooks_library
    | not_empty_hooks_libraries_list COMMA hooks_library
    ;

542
hooks_library: LCURLY_BRACKET {
543
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
544
545
546
547
548
549
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} hooks_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};

550
551
552
553
554
555
556
557
sub_hooks_library: LCURLY_BRACKET {
    // Parse the hooks-libraries list entry map
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
    ctx.stack_.push_back(m);
} hooks_params RCURLY_BRACKET {
    // parsing completed
};

558
hooks_params: hooks_param
559
            | hooks_params COMMA hooks_param
560
            ;
561

562
563
564
hooks_param: LIBRARY {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
565
    ElementPtr lib(new StringElement($4, ctx.loc2pos(@4)));
566
567
    ctx.stack_.back()->set("library", lib);
    ctx.leave(); 
568
569
570
};

// --- expired-leases-processing ------------------------
571
expired_leases_processing: EXPIRED_LEASES_PROCESSING {
572
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
573
574
    ctx.stack_.back()->set("expired-leases-processing", m);
    ctx.stack_.push_back(m);
575
576
    ctx.enter(ctx.NO_KEYWORD);
} COLON LCURLY_BRACKET expired_leases_params RCURLY_BRACKET {
577
    ctx.stack_.pop_back();
578
    ctx.leave();
579
580
581
};

expired_leases_params: expired_leases_param
582
583
                     | expired_leases_params COMMA expired_leases_param
                     ;
584
585
586
587
588

// This is a bit of a simplification. But it can also serve as an example.
// Instead of explicitly listing all allowed expired leases parameters, we
// simply say that all of them as integers.
expired_leases_param: STRING COLON INTEGER {
589
    ElementPtr value(new IntElement($3, ctx.loc2pos(@3)));
590
    ctx.stack_.back()->set($1, value);
591
592
593
}

// --- subnet6 ------------------------------------------
594
595
// This defines subnet6 as a list of maps.
// "subnet6": [ ... ]
596
subnet6_list: SUBNET6 {
597
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
598
    ctx.stack_.back()->set("subnet6", l);
599
    ctx.stack_.push_back(l);
600
601
    ctx.enter(ctx.SUBNET6);
} COLON LSQUARE_BRACKET subnet6_list_content RSQUARE_BRACKET {
602
    ctx.stack_.pop_back();
603
    ctx.leave();
604
605
606
607
608
};

// This defines the ... in "subnet6": [ ... ]
// It can either be empty (no subnets defined), have one subnet
// or have multiple subnets separate by comma.
609
subnet6_list_content: %empty
610
                    | not_empty_subnet6_list
611
                    ;
612

613
614
615
616
not_empty_subnet6_list: subnet6
                      | not_empty_subnet6_list COMMA subnet6
                      ;

617
618
// --- Subnet definitions -------------------------------

619
620
// This defines a single subnet, i.e. a single map with
// subnet6 array.
621
subnet6: LCURLY_BRACKET {
622
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
623
624
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
625
} subnet6_params RCURLY_BRACKET {
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
    // Once we reached this place, the subnet parsing is now complete.
    // If we want to, we can implement default values here.
    // In particular we can do things like this:
    // if (!ctx.stack_.back()->get("interface")) {
    //     ctx.stack_.back()->set("interface", StringElement("loopback"));
    // }
    //
    // We can also stack up one level (Dhcp6) and copy over whatever
    // global parameters we want to:
    // if (!ctx.stack_.back()->get("renew-timer")) {
    //     ElementPtr renew = ctx_stack_[...].get("renew-timer");
    //     if (renew) {
    //         ctx.stack_.back()->set("renew-timer", renew);
    //     }
    // }
641
    ctx.stack_.pop_back();
642
};
643

644
645
646
647
648
649
650
651
sub_subnet6: LCURLY_BRACKET {
    // Parse the subnet6 list entry map
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
    ctx.stack_.push_back(m);
} subnet6_params RCURLY_BRACKET {
    // parsing completed
};

652
// This defines that subnet can have one or more parameters.
653
subnet6_params: subnet6_param
654
655
              | subnet6_params COMMA subnet6_param
              ;
656

657
658
// This defines a list of allowed parameters for each subnet.
subnet6_param: option_data_list
659
660
661
662
663
664
665
             | pools_list
             | pd_pools_list
             | subnet
             | interface
             | id
             | client_class
             | reservations
666
             | unknown_map_entry
667
668
669
670
671
             ;

subnet: SUBNET {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
672
    ElementPtr subnet(new StringElement($4, ctx.loc2pos(@4)));
673
674
675
676
677
678
679
    ctx.stack_.back()->set("subnet", subnet);
    ctx.leave();
};

interface: INTERFACE {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
680
    ElementPtr iface(new StringElement($4, ctx.loc2pos(@4)));
681
682
683
684
685
686
687
    ctx.stack_.back()->set("interface", iface);
    ctx.leave();
};

client_class: CLIENT_CLASS {
    ctx.enter(ctx.CLIENT_CLASS);
} COLON STRING {
688
    ElementPtr cls(new StringElement($4, ctx.loc2pos(@4)));
689
690
    ctx.stack_.back()->set("client-class", cls);
    ctx.leave();
691
692
};

693
id: ID COLON INTEGER {
694
    ElementPtr id(new IntElement($3, ctx.loc2pos(@3)));
695
    ctx.stack_.back()->set("id", id);
696
};
697

698
699
700
701
// ---- option-data --------------------------

// This defines the "option-data": [ ... ] entry that may appear
// in several places, but most notably in subnet6 entries.
702
option_data_list: OPTION_DATA {
703
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
704
705
    ctx.stack_.back()->set("option-data", l);
    ctx.stack_.push_back(l);
706
    ctx.enter(ctx.OPTION_DATA);
707
708
} COLON LSQUARE_BRACKET option_data_list_content RSQUARE_BRACKET {
    ctx.stack_.pop_back();
709
    ctx.leave();
710
};
711
712
713

// This defines the content of option-data. It may be empty,
// have one entry or multiple entries separated by comma.
714
option_data_list_content: %empty
715
                        | not_empty_option_data_list
716
                        ;
717

718
719
720
721
not_empty_option_data_list: option_data_entry
                          | not_empty_option_data_list COMMA option_data_entry
                          ;

722
723
// This defines th content of a single entry { ... } within
// option-data list.
724
option_data_entry: LCURLY_BRACKET {
725
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
726
727
728
729
730
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} option_data_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};
731

732
733
734
735
736
737
738
739
sub_option_data: LCURLY_BRACKET {
    // Parse the option-data list entry map
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
    ctx.stack_.push_back(m);
} option_data_params RCURLY_BRACKET {
    // parsing completed
};

740
741
// This defines parameters specified inside the map that itself
// is an entry in option-data list.
742
743
option_data_params: %empty
                  | not_empty_option_data_params
744
                  ;
745

746
747
748
749
750
not_empty_option_data_params: option_data_param
    | not_empty_option_data_params COMMA option_data_param
    ;

option_data_param: option_data_name
751
752
753
754
                 | option_data_data
                 | option_data_code
                 | option_data_space
                 | option_data_csv_format
755
                 | unknown_map_entry
756
757
758
                 ;


759
option_data_name: name;
760

761
762
763
option_data_data: DATA {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
764
    ElementPtr data(new StringElement($4, ctx.loc2pos(@4)));
765
766
    ctx.stack_.back()->set("data", data);
    ctx.leave();
767
768
769
};

option_data_code: CODE COLON INTEGER {
770
    ElementPtr code(new IntElement($3, ctx.loc2pos(@3)));
771
    ctx.stack_.back()->set("code", code);
772
};
773

774
775
776
option_data_space: SPACE {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
777
    ElementPtr space(new StringElement($4, ctx.loc2pos(@4)));
778
779
    ctx.stack_.back()->set("space", space);
    ctx.leave();
780
781
};

782
option_data_csv_format: CSV_FORMAT COLON BOOLEAN {
783
    ElementPtr space(new BoolElement($3, ctx.loc2pos(@3)));
784
    ctx.stack_.back()->set("csv-format", space);
785
786
};

787
788
789
// ---- pools ------------------------------------

// This defines the "pools": [ ... ] entry that may appear in subnet6.
790
pools_list: POOLS {
791
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
792
793
    ctx.stack_.back()->set("pools", l);
    ctx.stack_.push_back(l);
794
795
    ctx.enter(ctx.POOLS);
} COLON LSQUARE_BRACKET pools_list_content RSQUARE_BRACKET {
796
    ctx.stack_.pop_back();
797
    ctx.leave();
798
};
799
800
801

// Pools may be empty, contain a single pool entry or multiple entries
// separate by commas.
802
pools_list_content: %empty
803
                  | not_empty_pools_list
804
                  ;
805

806
807
808
809
not_empty_pools_list: pool_list_entry
                    | not_empty_pools_list COMMA pool_list_entry
                    ;

810
pool_list_entry: LCURLY_BRACKET {
811
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
812
813
814
815
816
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} pool_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};
817

818
819
820
821
822
823
824
825
sub_pool6: LCURLY_BRACKET {
    // Parse the pool list entry map
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
    ctx.stack_.push_back(m);
} pool_params RCURLY_BRACKET {
    // parsing completed
};

826
pool_params: pool_param
827
828
829
830
831
           | pool_params COMMA pool_param
           ;

pool_param: pool_entry
          | option_data_list
832
          | unknown_map_entry
833
834
835
836
837
          ;

pool_entry: POOL {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
838
    ElementPtr pool(new StringElement($4, ctx.loc2pos(@4)));
839
840
841
    ctx.stack_.back()->set("pool", pool);
    ctx.leave();
};
842

843
844
// --- end of pools definition -------------------------------

845
// --- pd-pools ----------------------------------------------
846
pd_pools_list: PD_POOLS {
847
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
848
849
    ctx.stack_.back()->set("pd-pools", l);
    ctx.stack_.push_back(l);
850
851
    ctx.enter(ctx.PD_POOLS);
} COLON LSQUARE_BRACKET pd_pools_list_content RSQUARE_BRACKET {
852
    ctx.stack_.pop_back();
853
    ctx.leave();
854
855
856
857
};

// Pools may be empty, contain a single pool entry or multiple entries
// separate by commas.
858
pd_pools_list_content: %empty
859
                     | not_empty_pd_pools_list
860
                     ;
861

862
863
864
865
not_empty_pd_pools_list: pd_pool_entry
                       | not_empty_pd_pools_list COMMA pd_pool_entry
                       ;

866
pd_pool_entry: LCURLY_BRACKET {
867
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
868
869
870
871
872
873
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} pd_pool_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};

874
875
876
877
878
879
880
881
sub_pd_pool: LCURLY_BRACKET {
    // Parse the pd-pool list entry map
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
    ctx.stack_.push_back(m);
} pd_pool_params RCURLY_BRACKET {
    // parsing completed
};

882
pd_pool_params: pd_pool_param
883
884
              | pd_pool_params COMMA pd_pool_param
              ;
885
886

pd_pool_param: pd_prefix
887
888
889
             | pd_prefix_len
             | pd_delegated_len
             | option_data_list
890
             | unknown_map_entry
891
892
893
894
895
             ;

pd_prefix: PREFIX {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
896
    ElementPtr prf(new StringElement($4, ctx.loc2pos(@4)));
897
898
    ctx.stack_.back()->set("prefix", prf);
    ctx.leave();
899
900
901
}

pd_prefix_len: PREFIX_LEN COLON INTEGER {
902
    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
903
    ctx.stack_.back()->set("prefix-len", prf);
904
905
906
}

pd_delegated_len: DELEGATED_LEN COLON INTEGER {
907
    ElementPtr deleg(new IntElement($3, ctx.loc2pos(@3)));
908
    ctx.stack_.back()->set("delegated-len", deleg);
909
910
911
912
}

// --- end of pd-pools ---------------------------------------

913
// --- reservations ------------------------------------------
914
reservations: RESERVATIONS {
915
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
916
917
    ctx.stack_.back()->set("reservations", l);
    ctx.stack_.push_back(l);
918
919
    ctx.enter(ctx.RESERVATIONS);
} COLON LSQUARE_BRACKET reservations_list RSQUARE_BRACKET {
920
    ctx.stack_.pop_back();
921
922
    ctx.leave();
};
923

924
reservations_list: %empty
925
                 | not_empty_reservations_list
926
                 ;
927

928
929
930
931
not_empty_reservations_list: reservation
                           | not_empty_reservations_list COMMA reservation
                           ;

932
reservation: LCURLY_BRACKET {
933
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
934
935
936
937
938
939
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} reservation_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};

940
941
942
943
944
945
946
947
sub_reservation: LCURLY_BRACKET {
    // Parse the reservations list entry map
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
    ctx.stack_.push_back(m);
} reservation_params RCURLY_BRACKET {
    // parsing completed
};

948
949
reservation_params: %empty
                  | not_empty_reservation_params
950
                  ;
951

952
953
954
955
not_empty_reservation_params: reservation_param
    | not_empty_reservation_params COMMA reservation_param
    ;

956
// @todo probably need to add mac-address as well here
957
reservation_param: duid
958
959
960
961
962
963
                 | reservation_client_classes
                 | ip_addresses
                 | prefixes
                 | hw_address
                 | hostname
                 | option_data_list
964
                 | unknown_map_entry
965
966
967
                 ;

ip_addresses: IP_ADDRESSES {
968
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
969
970
    ctx.stack_.back()->set("ip-addresses", l);
    ctx.stack_.push_back(l);
971
972
    ctx.enter(ctx.NO_KEYWORD);
} COLON list2 {
973
    ctx.stack_.pop_back();
974
    ctx.leave();
975
976
};

977
prefixes: PREFIXES  {
978
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
979
980
    ctx.stack_.back()->set("prefixes", l);
    ctx.stack_.push_back(l);
981
982
    ctx.enter(ctx.NO_KEYWORD);
} COLON list2 {
983
    ctx.stack_.pop_back();
984
    ctx.leave();
985
986
};

987
988
989
duid: DUID {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
990
    ElementPtr d(new StringElement($4, ctx.loc2pos(@4)));
991
992
    ctx.stack_.back()->set("duid", d);
    ctx.leave();
993
994
};

995
996
997
hw_address: HW_ADDRESS {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
998
    ElementPtr hw(new StringElement($4, ctx.loc2pos(@4)));
999
1000
    ctx.stack_.back()->set("hw-address", hw);
    ctx.leave();
1001
1002
};

1003
1004
1005
hostname: HOSTNAME {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
1006
    ElementPtr host(new StringElement($4, ctx.loc2pos(@4)));
1007
1008
    ctx.stack_.back()->set("hostname", host);
    ctx.leave();
1009
1010
}

1011
reservation_client_classes: CLIENT_CLASSES {
1012
    ElementPtr c(new ListElement(ctx.loc2pos(@1)));
1013
1014
    ctx.stack_.back()->set("client-classes", c);
    ctx.stack_.push_back(c);
1015
1016
    ctx.enter(ctx.NO_KEYWORD);
} COLON list2 {
1017
    ctx.stack_.pop_back();
1018
1019
    ctx.leave();
};
1020
1021
1022
1023

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

// --- client classes ----------------------------------------
1024
client_classes: CLIENT_CLASSES {
1025
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
1026
1027
    ctx.stack_.back()->set("client-classes", l);
    ctx.stack_.push_back(l);
1028
1029
    ctx.enter(ctx.CLIENT_CLASSES);
} COLON LSQUARE_BRACKET client_classes_list RSQUARE_BRACKET {
1030
    ctx.stack_.pop_back();
1031
    ctx.leave();
1032
1033
1034
};

client_classes_list: client_class
1035
1036
                   | client_classes_list COMMA client_class
                   ;
1037
1038

client_class: LCURLY_BRACKET {
1039
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
1040
1041
1042
1043
1044
1045
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} client_class_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};

1046
1047
client_class_params: %empty
                   | not_empty_client_class_params
1048
                   ;
1049

1050
1051
1052
1053
1054
not_empty_client_class_params: client_class_param
    | not_empty_client_class_params COMMA client_class_param
    ;

client_class_param: client_class_name
1055
1056
                  | client_class_test
                  | option_data_list
1057
                  | unknown_map_entry
1058
1059
                  ;

1060
client_class_name: name;
1061

1062
1063
1064
client_class_test: TEST {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
1065
    ElementPtr test(new StringElement($4, ctx.loc2pos(@4)));
1066
    ctx.stack_.back()->set("test", test);
1067
    ctx.leave();
1068
1069
1070
1071
1072
}


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

1073
// --- server-id ---------------------------------------------
1074
server_id: SERVER_ID {
1075
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
1076
1077
    ctx.stack_.back()->set("server-id", m);
    ctx.stack_.push_back(m);
1078
1079
    ctx.enter(ctx.SERVER_ID);
} COLON LCURLY_BRACKET server_id_params RCURLY_BRACKET {
1080
    ctx.stack_.pop_back();
1081
    ctx.leave();
1082
1083
1084
};

server_id_params: server_id_param
1085
1086
                | server_id_params COMMA server_id_param
                ;
1087
1088

server_id_param: type
1089
1090
1091