dhcp6_parser.yy 41.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
%define api.token.prefix {TOKEN_}
// Tokens in an order which makes sense and related to the intented use.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
42
// Actual regexps for tokens are defined in dhcp6_lexer.ll.
43 44 45 46 47 48 49 50 51
%token
  END  0  "end of file"
  COMMA ","
  COLON ":"
  LSQUARE_BRACKET "["
  RSQUARE_BRACKET "]"
  LCURLY_BRACKET "{"
  RCURLY_BRACKET "}"
  NULL_TYPE "null"
52 53 54 55

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

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

66 67 68 69
  PREFERRED_LIFETIME "preferred-lifetime"
  VALID_LIFETIME "valid-lifetime"
  RENEW_TIMER "renew-timer"
  REBIND_TIMER "rebind-timer"
70
  DECLINE_PROBATION_PERIOD "decline-probation-period"
71
  SUBNET6 "subnet6"
72
  OPTION_DEF "option-def"
73 74 75 76
  OPTION_DATA "option-data"
  NAME "name"
  DATA "data"
  CODE "code"
77
  SPACE "space"
78
  CSV_FORMAT "csv-format"
79 80 81
  RECORD_TYPES "record-types"
  ENCAPSULATE "encapsulate"
  ARRAY "array"
82

83 84
  POOLS "pools"
  POOL "pool"
85 86 87 88 89
  PD_POOLS "pd-pools"
  PREFIX "prefix"
  PREFIX_LEN "prefix-len"
  DELEGATED_LEN "delegated-len"

90 91
  SUBNET "subnet"
  INTERFACE "interface"
92
  INTERFACE_ID "interface-id"
93
  ID "id"
94 95
  RAPID_COMMIT "rapid-commit"
  RESERVATION_MODE "reservation-mode"
96

97 98
  MAC_SOURCES "mac-sources"
  RELAY_SUPPLIED_OPTIONS "relay-supplied-options"
99
  HOST_RESERVATION_IDENTIFIERS "host-reservation-identifiers"
100

101 102 103 104 105
  CLIENT_CLASSES "client-classes"
  TEST "test"
  CLIENT_CLASS "client-class"

  RESERVATIONS "reservations"
106 107
  IP_ADDRESSES "ip-addresses"
  PREFIXES "prefixes"
108
  DUID "duid"
109 110
  HW_ADDRESS "hw-address"
  HOSTNAME "hostname"
111

112 113 114
  RELAY "relay"
  IP_ADDRESS "ip-address"

115 116 117 118 119 120 121 122 123
  HOOKS_LIBRARIES "hooks-libraries"
  LIBRARY "library"

  EXPIRED_LEASES_PROCESSING "expired-leases-processing"

  SERVER_ID "server-id"
  IDENTIFIER "identifier"
  HTYPE "htype"
  TIME "time"
124
  ENTERPRISE_ID "enterprise-id"
125

126
  DHCP4O6_PORT "dhcp4o6-port"
127 128 129 130 131 132 133
  VERSION "version"

  CONTROL_SOCKET "control-socket"
  SOCKET_TYPE "socket-type"
  SOCKET_NAME "socket-name"

  DHCP_DDNS "dhcp-ddns"
134

135 136 137 138 139 140
  LOGGING "Logging"
  LOGGERS "loggers"
  OUTPUT_OPTIONS "output_options"
  OUTPUT "output"
  DEBUGLEVEL "debuglevel"
  SEVERITY "severity"
141

142 143 144
  DHCP4 "Dhcp4"
  DHCPDDNS "DhcpDdns"

145 146 147
 // Not real tokens, just a way to signal what the parser is expected to
 // parse.
  TOPLEVEL_GENERIC_JSON
148
  TOPLEVEL_DHCP6
149
  SUB_DHCP6
150 151 152 153 154
  SUB_INTERFACES6
  SUB_SUBNET6
  SUB_POOL6
  SUB_PD_POOL
  SUB_RESERVATION
155
  SUB_OPTION_DEF
156 157
  SUB_OPTION_DATA
  SUB_HOOKS_LIBRARY
158
  SUB_JSON
159 160 161 162 163 164 165 166
;

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

%type <ElementPtr> value
167
%type <ElementPtr> version_value
168 169 170 171

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

%%
172

173 174
// The whole grammar starts with a map, because the config file
// constists of Dhcp, Logger and DhcpDdns entries in one big { }.
175
// We made the same for subparsers at the exception of the JSON value.
176 177
%start start;

178 179
start: TOPLEVEL_GENERIC_JSON { ctx.ctx_ = ctx.NO_KEYWORD; } map2
     | TOPLEVEL_DHCP6 { ctx.ctx_ = ctx.CONFIG; } syntax_map
180
     | SUB_DHCP6 { ctx.ctx_ = ctx.DHCP6; } sub_dhcp6
181 182 183 184 185
     | 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
186
     | SUB_OPTION_DEF { ctx.ctx_ = ctx.OPTION_DEF; } sub_option_def
187 188
     | SUB_OPTION_DATA { ctx.ctx_ = ctx.OPTION_DATA; } sub_option_data
     | SUB_HOOKS_LIBRARY { ctx.ctx_ = ctx.HOOKS_LIBRARIES; } sub_hooks_library
189
     | SUB_JSON { ctx.ctx_ = ctx.NO_KEYWORD; } sub_json
190
     ;
191

192 193
// ---- generic JSON parser ---------------------------------

194 195
// Note that ctx_ is NO_KEYWORD here

196
// Values rule
197 198 199 200 201
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))); }
202 203 204
     | map2 { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
     | list_generic { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
     ;
205

206 207 208 209 210
sub_json: value {
    // Push back the JSON value on the stack
    ctx.stack_.push_back($1);
};

211
map2: LCURLY_BRACKET {
212 213
    // This code is executed when we're about to start parsing
    // the content of the map
214
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
215 216 217 218 219 220
    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.
};
221 222

// Assignments rule
223
map_content: %empty // empty map
224
           | not_empty_map
225
           ;
226

227 228 229 230 231 232 233 234 235 236 237
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);
                  }
             ;

238
list_generic: LSQUARE_BRACKET {
239
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
240
    ctx.stack_.push_back(l);
241 242 243
} list_content RSQUARE_BRACKET {
    // list parsing complete. Put any sanity checking here
};
244 245 246

// This one is used in syntax parser.
list2: LSQUARE_BRACKET {
247 248 249
    // List parsing about to start
} list_content RSQUARE_BRACKET {
    // list parsing complete. Put any sanity checking here
250
    //ctx.stack_.pop_back();
251
};
252

253
list_content: %empty // Empty list
254
            | not_empty_list
255
            ;
256

257 258 259 260 261 262 263 264 265 266
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);
                  }
              ;

267 268 269 270
// ---- generic JSON parser ends here ----------------------------------

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

271 272 273 274 275 276 277 278 279
// 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.");
}


280 281
// This defines the top-level { } that holds Dhcp6, Dhcp4, DhcpDdns or Logging
// objects.
282
// ctx_ = CONFIG
283 284 285
syntax_map: LCURLY_BRACKET {
    // This code is executed when we're about to start parsing
    // the content of the map
286
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
287 288 289 290 291 292 293 294
    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
295 296 297
global_objects: global_object
              | global_objects COMMA global_object
              ;
298

299 300 301
// This represents a single top level entry, e.g. Dhcp6 or DhcpDdns.
global_object: dhcp6_object
             | logging_object
302 303 304
             | dhcp4_json_object
             | dhcpddns_json_object
             | unknown_map_entry
305 306
             ;

307
dhcp6_object: DHCP6 {
308 309
    // This code is executed when we're about to start parsing
    // the content of the map
310
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
311 312
    ctx.stack_.back()->set("Dhcp6", m);
    ctx.stack_.push_back(m);
313 314
    ctx.enter(ctx.DHCP6);
} COLON LCURLY_BRACKET global_params RCURLY_BRACKET {
315 316 317 318
    // 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();
319
    ctx.leave();
320 321
};

322 323 324 325 326 327 328 329 330 331
// subparser: similar to the corresponding rule but without parent
// so the stack is empty at the rule entry.
sub_dhcp6: LCURLY_BRACKET {
    // Parse the Dhcp6 map
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
    ctx.stack_.push_back(m);
} global_params RCURLY_BRACKET {
    // parsing completed
};

332
global_params: global_param
333 334
             | global_params COMMA global_param
             ;
335 336

// These are the parameters that are allowed in the top-level for
337
// Dhcp6.
338 339 340 341
global_param: preferred_lifetime
            | valid_lifetime
            | renew_timer
            | rebind_timer
342
            | decline_probation_period
343 344 345 346 347 348 349 350
            | subnet6_list
            | interfaces_config
            | lease_database
            | hosts_database
            | mac_sources
            | relay_supplied_options
            | host_reservation_identifiers
            | client_classes
351
            | option_def_list
352 353 354 355 356
            | option_data_list
            | hooks_libraries
            | expired_leases_processing
            | server_id
            | dhcp4o6_port
357 358
            | version
            | control_socket
359
            | dhcp_ddns
360
            | unknown_map_entry
361
            ;
362 363

preferred_lifetime: PREFERRED_LIFETIME COLON INTEGER {
364
    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
365
    ctx.stack_.back()->set("preferred-lifetime", prf);
366 367 368
};

valid_lifetime: VALID_LIFETIME COLON INTEGER {
369
    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
370
    ctx.stack_.back()->set("valid-lifetime", prf);
371 372 373
};

renew_timer: RENEW_TIMER COLON INTEGER {
374
    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
375
    ctx.stack_.back()->set("renew-timer", prf);
376 377
};

378
rebind_timer: REBIND_TIMER COLON INTEGER {
379
    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
380 381
    ctx.stack_.back()->set("rebind-timer", prf);
};
382

383 384 385 386 387
decline_probation_period: DECLINE_PROBATION_PERIOD COLON INTEGER {
    ElementPtr dpp(new IntElement($3, ctx.loc2pos(@3)));
    ctx.stack_.back()->set("decline-probation-period", dpp);
};

388
interfaces_config: INTERFACES_CONFIG {
389
    ElementPtr i(new MapElement(ctx.loc2pos(@1)));
390 391
    ctx.stack_.back()->set("interfaces-config", i);
    ctx.stack_.push_back(i);
392 393
    ctx.enter(ctx.INTERFACES_CONFIG);
} COLON LCURLY_BRACKET interface_config_map RCURLY_BRACKET {
394
    ctx.stack_.pop_back();
395
    ctx.leave();
396
};
397

398 399 400 401 402 403 404 405
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
};

406
interface_config_map: INTERFACES {
407
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
408 409
    ctx.stack_.back()->set("interfaces", l);
    ctx.stack_.push_back(l);
410 411 412 413 414
    ctx.enter(ctx.NO_KEYWORD);
} COLON list2 {
    ctx.stack_.pop_back();
    ctx.leave();
};
415 416

lease_database: LEASE_DATABASE {
417
    ElementPtr i(new MapElement(ctx.loc2pos(@1)));
418 419
    ctx.stack_.back()->set("lease-database", i);
    ctx.stack_.push_back(i);
420
    ctx.enter(ctx.LEASE_DATABASE);
421 422 423 424
} COLON LCURLY_BRACKET database_map_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
    ctx.leave();
};
425

426
hosts_database: HOSTS_DATABASE {
427
    ElementPtr i(new MapElement(ctx.loc2pos(@1)));
428 429
    ctx.stack_.back()->set("hosts-database", i);
    ctx.stack_.push_back(i);
430
    ctx.enter(ctx.HOSTS_DATABASE);
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
} 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
446 447
                  | lfc_interval
                  | unknown_map_entry
448
;
449

450 451 452
type: TYPE {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
453
    ElementPtr prf(new StringElement($4, ctx.loc2pos(@4)));
454
    ctx.stack_.back()->set("type", prf);
455
    ctx.leave();
456 457
};

458 459 460
user: USER {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
461
    ElementPtr user(new StringElement($4, ctx.loc2pos(@4)));
462
    ctx.stack_.back()->set("user", user);
463
    ctx.leave();
464 465
};

466 467 468
password: PASSWORD {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
469
    ElementPtr pwd(new StringElement($4, ctx.loc2pos(@4)));
470
    ctx.stack_.back()->set("password", pwd);
471
    ctx.leave();
472 473
};

474 475 476
host: HOST {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
477
    ElementPtr h(new StringElement($4, ctx.loc2pos(@4)));
478
    ctx.stack_.back()->set("host", h);
479
    ctx.leave();
480 481
};

482 483 484
name: NAME {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
485
    ElementPtr name(new StringElement($4, ctx.loc2pos(@4)));
486
    ctx.stack_.back()->set("name", name);
487
    ctx.leave();
488 489
};

490
persist: PERSIST COLON BOOLEAN {
491
    ElementPtr n(new BoolElement($3, ctx.loc2pos(@3)));
492 493 494
    ctx.stack_.back()->set("persist", n);
};

495
lfc_interval: LFC_INTERVAL COLON INTEGER {
496
    ElementPtr n(new IntElement($3, ctx.loc2pos(@3)));
497 498 499
    ctx.stack_.back()->set("lfc-interval", n);
};

500
mac_sources: MAC_SOURCES {
501
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
502 503
    ctx.stack_.back()->set("mac-sources", l);
    ctx.stack_.push_back(l);
504
    ctx.enter(ctx.MAC_SOURCES);
505
} COLON LSQUARE_BRACKET mac_sources_list RSQUARE_BRACKET {
506
    ctx.stack_.pop_back();
507
    ctx.leave();
508 509
};

510
mac_sources_list: mac_sources_value
511 512
                | mac_sources_list COMMA mac_sources_value
;
513

514 515
mac_sources_value: duid_id
                 | string_id
516 517
                 ;

518
duid_id : DUID {
519
    ElementPtr duid(new StringElement("duid", ctx.loc2pos(@1)));
520 521 522 523
    ctx.stack_.back()->add(duid);
};

string_id : STRING {
524
    ElementPtr duid(new StringElement($1, ctx.loc2pos(@1)));
525 526 527
    ctx.stack_.back()->add(duid);
};

528
host_reservation_identifiers: HOST_RESERVATION_IDENTIFIERS {
529
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
530 531
    ctx.stack_.back()->set("host-reservation-identifiers", l);
    ctx.stack_.push_back(l);
532 533
    ctx.enter(ctx.HOST_RESERVATION_IDENTIFIERS);    
} COLON LSQUARE_BRACKET host_reservation_identifiers_list RSQUARE_BRACKET {
534
    ctx.stack_.pop_back();
535
    ctx.leave();
536 537 538
};

host_reservation_identifiers_list: host_reservation_identifier
539 540
    | host_reservation_identifiers_list COMMA host_reservation_identifier
    ;
541

542 543
host_reservation_identifier: duid_id
                           | hw_address_id
544
                           ;
545

546
hw_address_id : HW_ADDRESS {
547
    ElementPtr hwaddr(new StringElement("hw-address", ctx.loc2pos(@1)));
548 549 550
    ctx.stack_.back()->add(hwaddr);
};

551
relay_supplied_options: RELAY_SUPPLIED_OPTIONS {
552
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
553 554
    ctx.stack_.back()->set("relay-supplied-options", l);
    ctx.stack_.push_back(l);
555
    ctx.enter(ctx.NO_KEYWORD);
556
} COLON list2 {
557
    ctx.stack_.pop_back();
558
    ctx.leave();
559 560
};

561
hooks_libraries: HOOKS_LIBRARIES {
562
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
563 564
    ctx.stack_.back()->set("hooks-libraries", l);
    ctx.stack_.push_back(l);
565 566
    ctx.enter(ctx.HOOKS_LIBRARIES);
} COLON LSQUARE_BRACKET hooks_libraries_list RSQUARE_BRACKET {
567
    ctx.stack_.pop_back();
568
    ctx.leave();
569 570
};

571
hooks_libraries_list: %empty
572
                    | not_empty_hooks_libraries_list
573
                    ;
574

575 576 577 578
not_empty_hooks_libraries_list: hooks_library
    | not_empty_hooks_libraries_list COMMA hooks_library
    ;

579
hooks_library: LCURLY_BRACKET {
580
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
581 582 583 584 585 586
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} hooks_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};

587 588 589 590 591 592 593 594
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
};

595
hooks_params: hooks_param
596
            | hooks_params COMMA hooks_param
597
            ;
598

599 600 601
hooks_param: LIBRARY {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
602
    ElementPtr lib(new StringElement($4, ctx.loc2pos(@4)));
603 604
    ctx.stack_.back()->set("library", lib);
    ctx.leave(); 
605 606 607
};

// --- expired-leases-processing ------------------------
608
expired_leases_processing: EXPIRED_LEASES_PROCESSING {
609
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
610 611
    ctx.stack_.back()->set("expired-leases-processing", m);
    ctx.stack_.push_back(m);
612 613
    ctx.enter(ctx.NO_KEYWORD);
} COLON LCURLY_BRACKET expired_leases_params RCURLY_BRACKET {
614
    ctx.stack_.pop_back();
615
    ctx.leave();
616 617 618
};

expired_leases_params: expired_leases_param
619 620
                     | expired_leases_params COMMA expired_leases_param
                     ;
621 622 623 624 625

// 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 {
626
    ElementPtr value(new IntElement($3, ctx.loc2pos(@3)));
627
    ctx.stack_.back()->set($1, value);
628 629 630
}

// --- subnet6 ------------------------------------------
631 632
// This defines subnet6 as a list of maps.
// "subnet6": [ ... ]
633
subnet6_list: SUBNET6 {
634
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
635
    ctx.stack_.back()->set("subnet6", l);
636
    ctx.stack_.push_back(l);
637 638
    ctx.enter(ctx.SUBNET6);
} COLON LSQUARE_BRACKET subnet6_list_content RSQUARE_BRACKET {
639
    ctx.stack_.pop_back();
640
    ctx.leave();
641 642 643 644 645
};

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

650 651 652 653
not_empty_subnet6_list: subnet6
                      | not_empty_subnet6_list COMMA subnet6
                      ;

654 655
// --- Subnet definitions -------------------------------

656 657
// This defines a single subnet, i.e. a single map with
// subnet6 array.
658
subnet6: LCURLY_BRACKET {
659
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
660 661
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
662
} subnet6_params RCURLY_BRACKET {
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
    // 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);
    //     }
    // }
678
    ctx.stack_.pop_back();
679
};
680

681 682 683 684 685 686 687 688
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
};

689
// This defines that subnet can have one or more parameters.
690
subnet6_params: subnet6_param
691 692
              | subnet6_params COMMA subnet6_param
              ;
693

694
// This defines a list of allowed parameters for each subnet.
695 696 697 698 699
subnet6_param: preferred_lifetime
             | valid_lifetime
             | renew_timer
             | rebind_timer
             | option_data_list
700 701 702 703
             | pools_list
             | pd_pools_list
             | subnet
             | interface
704
             | interface_id
705
             | id
706
             | rapid_commit
707 708
             | client_class
             | reservations
709 710
             | reservation_mode
             | relay
711
             | unknown_map_entry
712 713 714 715 716
             ;

subnet: SUBNET {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
717
    ElementPtr subnet(new StringElement($4, ctx.loc2pos(@4)));
718 719 720 721 722 723 724
    ctx.stack_.back()->set("subnet", subnet);
    ctx.leave();
};

interface: INTERFACE {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
725
    ElementPtr iface(new StringElement($4, ctx.loc2pos(@4)));
726 727 728 729
    ctx.stack_.back()->set("interface", iface);
    ctx.leave();
};

730 731 732 733 734 735 736 737
interface_id: INTERFACE_ID {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr iface(new StringElement($4, ctx.loc2pos(@4)));
    ctx.stack_.back()->set("interface-id", iface);
    ctx.leave();
};

738 739 740
client_class: CLIENT_CLASS {
    ctx.enter(ctx.CLIENT_CLASS);
} COLON STRING {
741
    ElementPtr cls(new StringElement($4, ctx.loc2pos(@4)));
742 743
    ctx.stack_.back()->set("client-class", cls);
    ctx.leave();
744 745
};

746 747 748 749 750 751 752 753
reservation_mode: RESERVATION_MODE {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr rm(new StringElement($4, ctx.loc2pos(@4)));
    ctx.stack_.back()->set("reservation-mode", rm);
    ctx.leave();
};

754
id: ID COLON INTEGER {
755
    ElementPtr id(new IntElement($3, ctx.loc2pos(@3)));
756
    ctx.stack_.back()->set("id", id);
757
};
758

759 760 761 762 763
rapid_commit: RAPID_COMMIT COLON BOOLEAN {
    ElementPtr rc(new BoolElement($3, ctx.loc2pos(@3)));
    ctx.stack_.back()->set("rapid-commit", rc);
};

764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
// ---- option-def --------------------------

// This defines the "option-def": [ ... ] entry that may appear
// at a global option.
option_def_list: OPTION_DEF {
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
    ctx.stack_.back()->set("option-def", l);
    ctx.stack_.push_back(l);
    ctx.enter(ctx.OPTION_DEF);
} COLON LSQUARE_BRACKET option_def_list_content RSQUARE_BRACKET {
    ctx.stack_.pop_back();
    ctx.leave();
};

// This defines the content of option-def. It may be empty,
// have one entry or multiple entries separated by comma.
option_def_list_content: %empty
                       | not_empty_option_def_list
                       ;

not_empty_option_def_list: option_def_entry
                         | not_empty_option_def_list COMMA option_def_entry
                          ;

// This defines the content of a single entry { ... } within
// option-def list.
option_def_entry: LCURLY_BRACKET {
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} option_def_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};

sub_option_def: LCURLY_BRACKET {
    // Parse the option-def list entry map
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
    ctx.stack_.push_back(m);
} option_def_params RCURLY_BRACKET {
    // parsing completed
};

// This defines parameters specified inside the map that itself
// is an entry in option-def list.
option_def_params: %empty
                 | not_empty_option_def_params
                 ;

not_empty_option_def_params: option_def_param
                           | not_empty_option_def_params COMMA option_def_param
                           ;

option_def_param: option_def_name
                | option_def_code
                | option_def_type
819
                | option_def_record_types
820
                | option_def_space
821 822
                | option_def_encapsulate
                | option_def_array
823 824 825 826 827 828 829 830 831 832 833 834 835 836
                | unknown_map_entry
                ;

option_def_name: name;

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

option_def_code: code;

option_def_type: type;

837 838 839 840 841 842 843 844
option_def_record_types: RECORD_TYPES {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr rtypes(new StringElement($4, ctx.loc2pos(@4)));
    ctx.stack_.back()->set("record-types", rtypes);
    ctx.leave();
};

845 846 847 848 849 850 851 852 853 854
space: SPACE {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr space(new StringElement($4, ctx.loc2pos(@4)));
    ctx.stack_.back()->set("space", space);
    ctx.leave();
};

option_def_space: space;

855 856 857 858 859 860
option_def_encapsulate: ENCAPSULATE {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr encap(new StringElement($4, ctx.loc2pos(@4)));
    ctx.stack_.back()->set("encapsulate", encap);
    ctx.leave();
861 862
};

863 864 865 866
option_def_array: ARRAY COLON BOOLEAN {
    ElementPtr array(new BoolElement($3, ctx.loc2pos(@3)));
    ctx.stack_.back()->set("array", array);
};
867

868 869 870 871
// ---- option-data --------------------------

// This defines the "option-data": [ ... ] entry that may appear
// in several places, but most notably in subnet6 entries.
872
option_data_list: OPTION_DATA {
873
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
874 875
    ctx.stack_.back()->set("option-data", l);
    ctx.stack_.push_back(l);
876
    ctx.enter(ctx.OPTION_DATA);
877 878
} COLON LSQUARE_BRACKET option_data_list_content RSQUARE_BRACKET {
    ctx.stack_.pop_back();
879
    ctx.leave();
880
};
881 882 883

// This defines the content of option-data. It may be empty,
// have one entry or multiple entries separated by comma.
884
option_data_list_content: %empty
885
                        | not_empty_option_data_list
886
                        ;
887

888 889 890 891
not_empty_option_data_list: option_data_entry
                          | not_empty_option_data_list COMMA option_data_entry
                          ;

892 893
// This defines th content of a single entry { ... } within
// option-data list.
894
option_data_entry: LCURLY_BRACKET {
895
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
896 897 898 899 900
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} option_data_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};
901

902 903 904 905 906 907 908 909
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
};

910 911
// This defines parameters specified inside the map that itself
// is an entry in option-data list.
912 913
option_data_params: %empty
                  | not_empty_option_data_params
914
                  ;
915

916 917 918 919 920
not_empty_option_data_params: option_data_param
    | not_empty_option_data_params COMMA option_data_param
    ;

option_data_param: option_data_name
921 922 923 924
                 | option_data_data
                 | option_data_code
                 | option_data_space
                 | option_data_csv_format
925
                 | unknown_map_entry
926 927
                 ;

928
option_data_name: name;
929

930 931 932
option_data_data: DATA {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
933
    ElementPtr data(new StringElement($4, ctx.loc2pos(@4)));
934 935
    ctx.stack_.back()->set("data", data);
    ctx.leave();
936 937
};

938
option_data_code: code;
939

940
option_data_space: space;
941

942 943 944 945
option_data_csv_format: CSV_FORMAT COLON BOOLEAN {
    ElementPtr space(new BoolElement($3, ctx.loc2pos(@3)));
    ctx.stack_.back()->set("csv-format", space);
};
946

947 948 949
// ---- pools ------------------------------------

// This defines the "pools": [ ... ] entry that may appear in subnet6.
950
pools_list: POOLS {
951
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
952 953
    ctx.stack_.back()->set("pools", l);
    ctx.stack_.push_back(l);
954 955
    ctx.enter(ctx.POOLS);
} COLON LSQUARE_BRACKET pools_list_content RSQUARE_BRACKET {
956
    ctx.stack_.pop_back();
957
    ctx.leave();
958
};
959 960 961

// Pools may be empty, contain a single pool entry or multiple entries
// separate by commas.
962
pools_list_content: %empty
963
                  | not_empty_pools_list
964
                  ;
965

966 967 968 969
not_empty_pools_list: pool_list_entry
                    | not_empty_pools_list COMMA pool_list_entry
                    ;

970
pool_list_entry: LCURLY_BRACKET {
971
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
972 973 974 975 976
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} pool_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};
977

978 979 980 981 982 983 984 985
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
};

986
pool_params: pool_param