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

38

39 40
%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
41
// Actual regexps for tokens are defined in dhcp6_lexer.ll.
42 43 44 45 46 47 48 49 50
%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
  READONLY "readonly"
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
  PD_POOLS "pd-pools"
  PREFIX "prefix"
  PREFIX_LEN "prefix-len"
88 89
  EXCLUDED_PREFIX "excluded-prefix"
  EXCLUDED_PREFIX_LEN "excluded-prefix-len"
90 91
  DELEGATED_LEN "delegated-len"

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

99 100
  MAC_SOURCES "mac-sources"
  RELAY_SUPPLIED_OPTIONS "relay-supplied-options"
101
  HOST_RESERVATION_IDENTIFIERS "host-reservation-identifiers"
102

103 104 105 106 107
  CLIENT_CLASSES "client-classes"
  TEST "test"
  CLIENT_CLASS "client-class"

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

114 115 116
  RELAY "relay"
  IP_ADDRESS "ip-address"

117 118
  HOOKS_LIBRARIES "hooks-libraries"
  LIBRARY "library"
119
  PARAMETERS "parameters"
120 121 122 123 124 125 126

  EXPIRED_LEASES_PROCESSING "expired-leases-processing"

  SERVER_ID "server-id"
  IDENTIFIER "identifier"
  HTYPE "htype"
  TIME "time"
127
  ENTERPRISE_ID "enterprise-id"
128

129
  DHCP4O6_PORT "dhcp4o6-port"
130 131 132 133 134 135 136
  VERSION "version"

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

  DHCP_DDNS "dhcp-ddns"
137

Tomek Mrugalski's avatar
Tomek Mrugalski committed
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
 /// @todo: Implement proper parsing for those parameters in Dhcp6/dhcp-ddns/*.
 /// This should be part of the #5043 ticket. Listing the keywords here for
 /// completeness.

 // These are tokens defined in Dhcp6/dhcp-ddns/*
 // They're not
 //  ENABLE_UPDATES "enable-updates"
 //  SERVER_IP "server-ip"
 //  SENDER_IP "sender-ip"
 //  SENDER_PORT "sender-port"
 //  MAX_QUEUE_SIZE "max-queue-size"
 //  NCR_PROTOCOL "ncr-protocol"
 //  NCR_FORMAT "ncr-format"
 //  ALWAYS_INCLUDE_FQDN "always-include-fqdn"
 //  OVERRDIDE_NO_UPDATE "override-no-update"
 //  OVERRDIDE_CLIENT_UPDATE "override-client-update"
 //  REPLACE_CLIENT_NAME "replace-client-name"
 //  GENERATED_PREFIX "generated-prefix"
 //  QUALIFYING_SUFFIX "qualifying-suffix"

158 159 160 161 162 163
  LOGGING "Logging"
  LOGGERS "loggers"
  OUTPUT_OPTIONS "output_options"
  OUTPUT "output"
  DEBUGLEVEL "debuglevel"
  SEVERITY "severity"
164

165 166 167
  DHCP4 "Dhcp4"
  DHCPDDNS "DhcpDdns"

168 169
 // Not real tokens, just a way to signal what the parser is expected to
 // parse.
170
  TOPLEVEL_JSON
171
  TOPLEVEL_DHCP6
172
  SUB_DHCP6
173 174 175 176 177
  SUB_INTERFACES6
  SUB_SUBNET6
  SUB_POOL6
  SUB_PD_POOL
  SUB_RESERVATION
178
  SUB_OPTION_DEF
179 180
  SUB_OPTION_DATA
  SUB_HOOKS_LIBRARY
181 182 183 184 185 186 187 188
;

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

%type <ElementPtr> value
189
%type <ElementPtr> version_value
190 191 192 193

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

%%
194

195 196
// The whole grammar starts with a map, because the config file
// constists of Dhcp, Logger and DhcpDdns entries in one big { }.
197
// We made the same for subparsers at the exception of the JSON value.
198 199
%start start;

200
start: TOPLEVEL_JSON { ctx.ctx_ = ctx.NO_KEYWORD; } sub_json
201
     | TOPLEVEL_DHCP6 { ctx.ctx_ = ctx.CONFIG; } syntax_map
202
     | SUB_DHCP6 { ctx.ctx_ = ctx.DHCP6; } sub_dhcp6
203 204 205 206 207
     | 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
208
     | SUB_OPTION_DEF { ctx.ctx_ = ctx.OPTION_DEF; } sub_option_def
209 210
     | SUB_OPTION_DATA { ctx.ctx_ = ctx.OPTION_DATA; } sub_option_data
     | SUB_HOOKS_LIBRARY { ctx.ctx_ = ctx.HOOKS_LIBRARIES; } sub_hooks_library
211
     ;
212

213 214
// ---- generic JSON parser ---------------------------------

215 216
// Note that ctx_ is NO_KEYWORD here

217
// Values rule
218 219 220 221 222
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))); }
223 224 225
     | map2 { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
     | list_generic { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
     ;
226

227 228 229 230 231
sub_json: value {
    // Push back the JSON value on the stack
    ctx.stack_.push_back($1);
};

232
map2: LCURLY_BRACKET {
233 234
    // This code is executed when we're about to start parsing
    // the content of the map
235
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
236 237 238 239 240 241
    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.
};
242 243

// Assignments rule
244
map_content: %empty // empty map
245
           | not_empty_map
246
           ;
247

248 249 250 251 252 253 254 255 256 257 258
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);
                  }
             ;

259
list_generic: LSQUARE_BRACKET {
260
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
261
    ctx.stack_.push_back(l);
262 263 264
} list_content RSQUARE_BRACKET {
    // list parsing complete. Put any sanity checking here
};
265 266 267

// This one is used in syntax parser.
list2: LSQUARE_BRACKET {
268 269 270
    // List parsing about to start
} list_content RSQUARE_BRACKET {
    // list parsing complete. Put any sanity checking here
271
    //ctx.stack_.pop_back();
272
};
273

274
list_content: %empty // Empty list
275
            | not_empty_list
276
            ;
277

278 279 280 281 282 283 284 285 286 287
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);
                  }
              ;

288 289 290 291
// ---- generic JSON parser ends here ----------------------------------

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

292 293
// Unknown keyword in a map
unknown_map_entry: STRING COLON {
294
    const std::string& where = ctx.contextName();
295 296 297 298 299 300
    const std::string& keyword = $1;
    error(@1,
          "got unexpected keyword \"" + keyword + "\" in " + where + " map.");
}


301 302 303 304 305
// 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
306
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
307 308 309 310 311 312 313 314
    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
315 316 317
global_objects: global_object
              | global_objects COMMA global_object
              ;
318

319 320 321
// This represents a single top level entry, e.g. Dhcp6 or DhcpDdns.
global_object: dhcp6_object
             | logging_object
322 323 324
             | dhcp4_json_object
             | dhcpddns_json_object
             | unknown_map_entry
325 326
             ;

327
dhcp6_object: DHCP6 {
328 329
    // This code is executed when we're about to start parsing
    // the content of the map
330
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
331 332
    ctx.stack_.back()->set("Dhcp6", m);
    ctx.stack_.push_back(m);
333 334
    ctx.enter(ctx.DHCP6);
} COLON LCURLY_BRACKET global_params RCURLY_BRACKET {
335 336 337 338
    // 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();
339
    ctx.leave();
340 341
};

342 343 344 345 346 347 348 349 350 351
// 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
};

352
global_params: global_param
353 354
             | global_params COMMA global_param
             ;
355 356

// These are the parameters that are allowed in the top-level for
357
// Dhcp6.
358 359 360 361
global_param: preferred_lifetime
            | valid_lifetime
            | renew_timer
            | rebind_timer
362
            | decline_probation_period
363 364 365 366 367 368 369 370
            | subnet6_list
            | interfaces_config
            | lease_database
            | hosts_database
            | mac_sources
            | relay_supplied_options
            | host_reservation_identifiers
            | client_classes
371
            | option_def_list
372 373 374 375 376
            | option_data_list
            | hooks_libraries
            | expired_leases_processing
            | server_id
            | dhcp4o6_port
377 378
            | version
            | control_socket
379
            | dhcp_ddns
380
            | unknown_map_entry
381
            ;
382 383

preferred_lifetime: PREFERRED_LIFETIME COLON INTEGER {
384
    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
385
    ctx.stack_.back()->set("preferred-lifetime", prf);
386 387 388
};

valid_lifetime: VALID_LIFETIME COLON INTEGER {
389
    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
390
    ctx.stack_.back()->set("valid-lifetime", prf);
391 392 393
};

renew_timer: RENEW_TIMER COLON INTEGER {
394
    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
395
    ctx.stack_.back()->set("renew-timer", prf);
396 397
};

398
rebind_timer: REBIND_TIMER COLON INTEGER {
399
    ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
400 401
    ctx.stack_.back()->set("rebind-timer", prf);
};
402

403 404 405 406 407
decline_probation_period: DECLINE_PROBATION_PERIOD COLON INTEGER {
    ElementPtr dpp(new IntElement($3, ctx.loc2pos(@3)));
    ctx.stack_.back()->set("decline-probation-period", dpp);
};

408
interfaces_config: INTERFACES_CONFIG {
409
    ElementPtr i(new MapElement(ctx.loc2pos(@1)));
410 411
    ctx.stack_.back()->set("interfaces-config", i);
    ctx.stack_.push_back(i);
412 413
    ctx.enter(ctx.INTERFACES_CONFIG);
} COLON LCURLY_BRACKET interface_config_map RCURLY_BRACKET {
414
    ctx.stack_.pop_back();
415
    ctx.leave();
416
};
417

418 419 420 421 422 423 424 425
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
};

426
interface_config_map: INTERFACES {
427
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
428 429
    ctx.stack_.back()->set("interfaces", l);
    ctx.stack_.push_back(l);
430 431 432 433 434
    ctx.enter(ctx.NO_KEYWORD);
} COLON list2 {
    ctx.stack_.pop_back();
    ctx.leave();
};
435 436

lease_database: LEASE_DATABASE {
437
    ElementPtr i(new MapElement(ctx.loc2pos(@1)));
438 439
    ctx.stack_.back()->set("lease-database", i);
    ctx.stack_.push_back(i);
440
    ctx.enter(ctx.LEASE_DATABASE);
441 442 443 444
} COLON LCURLY_BRACKET database_map_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
    ctx.leave();
};
445

446
hosts_database: HOSTS_DATABASE {
447
    ElementPtr i(new MapElement(ctx.loc2pos(@1)));
448 449
    ctx.stack_.back()->set("hosts-database", i);
    ctx.stack_.push_back(i);
450
    ctx.enter(ctx.HOSTS_DATABASE);
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
} 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
466
                  | lfc_interval
467
                  | readonly
468
                  | unknown_map_entry
469
;
470

471 472 473
type: TYPE {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
474
    ElementPtr prf(new StringElement($4, ctx.loc2pos(@4)));
475
    ctx.stack_.back()->set("type", prf);
476
    ctx.leave();
477 478
};

479 480 481
user: USER {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
482
    ElementPtr user(new StringElement($4, ctx.loc2pos(@4)));
483
    ctx.stack_.back()->set("user", user);
484
    ctx.leave();
485 486
};

487 488 489
password: PASSWORD {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
490
    ElementPtr pwd(new StringElement($4, ctx.loc2pos(@4)));
491
    ctx.stack_.back()->set("password", pwd);
492
    ctx.leave();
493 494
};

495 496 497
host: HOST {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
498
    ElementPtr h(new StringElement($4, ctx.loc2pos(@4)));
499
    ctx.stack_.back()->set("host", h);
500
    ctx.leave();
501 502
};

503 504 505
name: NAME {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
506
    ElementPtr name(new StringElement($4, ctx.loc2pos(@4)));
507
    ctx.stack_.back()->set("name", name);
508
    ctx.leave();
509 510
};

511
persist: PERSIST COLON BOOLEAN {
512
    ElementPtr n(new BoolElement($3, ctx.loc2pos(@3)));
513 514 515
    ctx.stack_.back()->set("persist", n);
};

516
lfc_interval: LFC_INTERVAL COLON INTEGER {
517
    ElementPtr n(new IntElement($3, ctx.loc2pos(@3)));
518 519 520
    ctx.stack_.back()->set("lfc-interval", n);
};

521 522 523 524 525
readonly: READONLY COLON BOOLEAN {
    ElementPtr n(new BoolElement($3, ctx.loc2pos(@3)));
    ctx.stack_.back()->set("readonly", n);
};

526
mac_sources: MAC_SOURCES {
527
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
528 529
    ctx.stack_.back()->set("mac-sources", l);
    ctx.stack_.push_back(l);
530
    ctx.enter(ctx.MAC_SOURCES);
531
} COLON LSQUARE_BRACKET mac_sources_list RSQUARE_BRACKET {
532
    ctx.stack_.pop_back();
533
    ctx.leave();
534 535
};

536
mac_sources_list: mac_sources_value
537 538
                | mac_sources_list COMMA mac_sources_value
;
539

540 541
mac_sources_value: duid_id
                 | string_id
542 543
                 ;

544
duid_id : DUID {
545
    ElementPtr duid(new StringElement("duid", ctx.loc2pos(@1)));
546 547 548 549
    ctx.stack_.back()->add(duid);
};

string_id : STRING {
550
    ElementPtr duid(new StringElement($1, ctx.loc2pos(@1)));
551 552 553
    ctx.stack_.back()->add(duid);
};

554
host_reservation_identifiers: HOST_RESERVATION_IDENTIFIERS {
555
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
556 557
    ctx.stack_.back()->set("host-reservation-identifiers", l);
    ctx.stack_.push_back(l);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
558
    ctx.enter(ctx.HOST_RESERVATION_IDENTIFIERS);
559
} COLON LSQUARE_BRACKET host_reservation_identifiers_list RSQUARE_BRACKET {
560
    ctx.stack_.pop_back();
561
    ctx.leave();
562 563 564
};

host_reservation_identifiers_list: host_reservation_identifier
565 566
    | host_reservation_identifiers_list COMMA host_reservation_identifier
    ;
567

568 569
host_reservation_identifier: duid_id
                           | hw_address_id
570
                           ;
571

572
hw_address_id : HW_ADDRESS {
573
    ElementPtr hwaddr(new StringElement("hw-address", ctx.loc2pos(@1)));
574 575 576
    ctx.stack_.back()->add(hwaddr);
};

577
relay_supplied_options: RELAY_SUPPLIED_OPTIONS {
578
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
579 580
    ctx.stack_.back()->set("relay-supplied-options", l);
    ctx.stack_.push_back(l);
581
    ctx.enter(ctx.NO_KEYWORD);
582
} COLON list2 {
583
    ctx.stack_.pop_back();
584
    ctx.leave();
585 586
};

587
hooks_libraries: HOOKS_LIBRARIES {
588
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
589 590
    ctx.stack_.back()->set("hooks-libraries", l);
    ctx.stack_.push_back(l);
591 592
    ctx.enter(ctx.HOOKS_LIBRARIES);
} COLON LSQUARE_BRACKET hooks_libraries_list RSQUARE_BRACKET {
593
    ctx.stack_.pop_back();
594
    ctx.leave();
595 596
};

597
hooks_libraries_list: %empty
598
                    | not_empty_hooks_libraries_list
599
                    ;
600

601 602 603 604
not_empty_hooks_libraries_list: hooks_library
    | not_empty_hooks_libraries_list COMMA hooks_library
    ;

605
hooks_library: LCURLY_BRACKET {
606
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
607 608 609 610 611 612
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} hooks_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};

613 614 615 616 617 618 619 620
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
};

621
hooks_params: hooks_param
622
            | hooks_params COMMA hooks_param
623
            ;
624

625 626 627 628
hooks_param: library
           | parameters;

library: LIBRARY {
629 630
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
631
    ElementPtr lib(new StringElement($4, ctx.loc2pos(@4)));
632
    ctx.stack_.back()->set("library", lib);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
633
    ctx.leave();
634 635
};

636 637 638 639 640 641 642
parameters: PARAMETERS {
    ctx.enter(ctx.NO_KEYWORD);
} COLON value {
    ctx.stack_.back()->set("parameters", $4);
    ctx.leave();
}

643
// --- expired-leases-processing ------------------------
644
expired_leases_processing: EXPIRED_LEASES_PROCESSING {
645
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
646 647
    ctx.stack_.back()->set("expired-leases-processing", m);
    ctx.stack_.push_back(m);
648 649
    ctx.enter(ctx.NO_KEYWORD);
} COLON LCURLY_BRACKET expired_leases_params RCURLY_BRACKET {
650
    ctx.stack_.pop_back();
651
    ctx.leave();
652 653 654
};

expired_leases_params: expired_leases_param
655 656
                     | expired_leases_params COMMA expired_leases_param
                     ;
657 658 659 660 661

// 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 {
662
    ElementPtr value(new IntElement($3, ctx.loc2pos(@3)));
663
    ctx.stack_.back()->set($1, value);
664 665 666
}

// --- subnet6 ------------------------------------------
667 668
// This defines subnet6 as a list of maps.
// "subnet6": [ ... ]
669
subnet6_list: SUBNET6 {
670
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
671
    ctx.stack_.back()->set("subnet6", l);
672
    ctx.stack_.push_back(l);
673 674
    ctx.enter(ctx.SUBNET6);
} COLON LSQUARE_BRACKET subnet6_list_content RSQUARE_BRACKET {
675
    ctx.stack_.pop_back();
676
    ctx.leave();
677 678 679 680 681
};

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

686 687 688 689
not_empty_subnet6_list: subnet6
                      | not_empty_subnet6_list COMMA subnet6
                      ;

690 691
// --- Subnet definitions -------------------------------

692 693
// This defines a single subnet, i.e. a single map with
// subnet6 array.
694
subnet6: LCURLY_BRACKET {
695
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
696 697
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
698
} subnet6_params RCURLY_BRACKET {
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
    // 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);
    //     }
    // }
714
    ctx.stack_.pop_back();
715
};
716

717 718 719 720 721 722 723 724
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
};

725
// This defines that subnet can have one or more parameters.
726
subnet6_params: subnet6_param
727 728
              | subnet6_params COMMA subnet6_param
              ;
729

730
// This defines a list of allowed parameters for each subnet.
731 732 733 734 735
subnet6_param: preferred_lifetime
             | valid_lifetime
             | renew_timer
             | rebind_timer
             | option_data_list
736 737 738 739
             | pools_list
             | pd_pools_list
             | subnet
             | interface
740
             | interface_id
741
             | id
742
             | rapid_commit
743 744
             | client_class
             | reservations
745 746
             | reservation_mode
             | relay
747
             | unknown_map_entry
748 749 750 751 752
             ;

subnet: SUBNET {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
753
    ElementPtr subnet(new StringElement($4, ctx.loc2pos(@4)));
754 755 756 757 758 759 760
    ctx.stack_.back()->set("subnet", subnet);
    ctx.leave();
};

interface: INTERFACE {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
761
    ElementPtr iface(new StringElement($4, ctx.loc2pos(@4)));
762 763 764 765
    ctx.stack_.back()->set("interface", iface);
    ctx.leave();
};

766 767 768 769 770 771 772 773
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();
};

774 775 776
client_class: CLIENT_CLASS {
    ctx.enter(ctx.CLIENT_CLASS);
} COLON STRING {
777
    ElementPtr cls(new StringElement($4, ctx.loc2pos(@4)));
778 779
    ctx.stack_.back()->set("client-class", cls);
    ctx.leave();
780 781
};

782 783 784 785 786 787 788 789
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();
};

790
id: ID COLON INTEGER {
791
    ElementPtr id(new IntElement($3, ctx.loc2pos(@3)));
792
    ctx.stack_.back()->set("id", id);
793
};
794

795 796 797 798 799
rapid_commit: RAPID_COMMIT COLON BOOLEAN {
    ElementPtr rc(new BoolElement($3, ctx.loc2pos(@3)));
    ctx.stack_.back()->set("rapid-commit", rc);
};

800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
// ---- 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();
};

834 835 836
// This defines the top level scope when the parser is told to parse a single
// option definition. It's almost exactly the same as option_def_entry, except
// that it does leave its value on stack.
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
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
858
                | option_def_record_types
859
                | option_def_space
860 861
                | option_def_encapsulate
                | option_def_array
862 863 864 865 866 867 868 869 870 871 872 873 874 875
                | 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;

876 877 878 879 880 881 882 883
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();
};

884 885 886 887 888 889 890 891 892 893
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;

894 895 896 897 898 899
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();
900 901
};

902 903 904 905
option_def_array: ARRAY COLON BOOLEAN {
    ElementPtr array(new BoolElement($3, ctx.loc2pos(@3)));
    ctx.stack_.back()->set("array", array);
};
906

907 908 909 910
// ---- option-data --------------------------

// This defines the "option-data": [ ... ] entry that may appear
// in several places, but most notably in subnet6 entries.
911
option_data_list: OPTION_DATA {
912
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
913 914
    ctx.stack_.back()->set("option-data", l);
    ctx.stack_.push_back(l);
915
    ctx.enter(ctx.OPTION_DATA);
916 917
} COLON LSQUARE_BRACKET option_data_list_content RSQUARE_BRACKET {
    ctx.stack_.pop_back();
918
    ctx.leave();
919
};
920 921 922

// This defines the content of option-data. It may be empty,
// have one entry or multiple entries separated by comma.
923
option_data_list_content: %empty
924
                        | not_empty_option_data_list
925
                        ;
926

927 928
// This defines the content of option-data list. It can either
// be a single value or multiple entries separated by comma.
929 930 931 932
not_empty_option_data_list: option_data_entry
                          | not_empty_option_data_list COMMA option_data_entry
                          ;

933 934
// This defines th content of a single entry { ... } within
// option-data list.
935
option_data_entry: LCURLY_BRACKET {
936
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
937 938 939 940 941
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} option_data_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};
942

943 944 945
// This defines the top level scope when the parser is told to parse a single
// option data. It's almost exactly the same as option_data_entry, except
// that it does leave its value on stack.
946 947 948 949 950 951 952 953
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
};

954
// This defines parameters specified inside the map that itself
Tomek Mrugalski's avatar
Tomek Mrugalski committed
955 956
// is an entry in option-data list. It can either be empty
// or have a non-empty list of parameters.
957 958
option_data_params: %empty
                  | not_empty_option_data_params
959
                  ;
960

Tomek Mrugalski's avatar
Tomek Mrugalski committed
961 962
// Those parameters can either be a single parameter or
// a list of parameters separated by comma.
963 964 965 966
not_empty_option_data_params: option_data_param
    | not_empty_option_data_params COMMA option_data_param
    ;

Tomek Mrugalski's avatar
Tomek Mrugalski committed
967 968
// Each single option-data parameter can be one of the following
// expressions.
969
option_data_param: option_data_name
970 971 972 973
                 | option_data_data
                 | option_data_code
                 | option_data_space
                 | option_data_csv_format
974
                 | unknown_map_entry
975 976
                 ;

977
option_data_name: name;
978

979 980 981
option_data_data: DATA {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
982
    ElementPtr data(new StringElement($4, ctx.loc2pos(@4)));
983 984
    ctx.stack_.back()->set("data", data);
    ctx.leave();
985 986
};

987
option_data_code: code;
988

989
option_data_space: space;
990

991 992 993 994
option_data_csv_format: CSV_FORMAT COLON BOOLEAN {
    ElementPtr space(new BoolElement($3, ctx.loc2pos(@3)));
    ctx.stack_.back()->set("csv-format", space);
};
995

996 997 998
// ---- pools ------------------------------------

// This defines the "pools": [ ... ] entry that may appear in subnet6.
999
pools_list: POOLS {
1000
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
1001 1002
    ctx.stack_.back()->set("pools", l);
    ctx.stack_.push_back(l);
1003 1004
    ctx.enter(ctx.POOLS);
} COLON LSQUARE_BRACKET pools_list_content RSQUARE_BRACKET {
1005
    ctx.stack_.pop_back();
1006
    ctx.leave();
1007
};
1008 1009 1010

// Pools may be empty, contain a single pool entry or multiple entries
// separate by commas.
1011
pools_list_content: %empty
1012
                  | not_empty_pools_list
1013
                  ;
1014

1015 1016 1017 1018
not_empty_pools_list: pool_list_entry
                    | not_empty_pools_list COMMA pool_list_entry
                    ;

1019
pool_list_entry: LCURLY_BRACKET {
1020
    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
1021 1022 1023 1024 1025
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} pool_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};
1026

1027 1028 1029 1030 1031 1032 1033 1034
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
};

1035
pool_params: pool_param
1036 1037 1038 1039 1040
           | pool_params COMMA pool_param
           ;

pool_param: pool_entry
          | option_data_list
1041
          | unknown_map_entry
1042 1043 1044 1045 1046
          ;

pool_entry: POOL {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
1047
    ElementPtr pool(new StringElement($4, ctx.loc2pos(@4)));
1048 1049 1050
    ctx.stack_.back()->set("pool", pool);
    ctx.leave();
};
1051

1052 1053
// --- end of pools definition -------------------------------

1054
// --- pd-pools ----------------------------------------------
1055
pd_pools_list: PD_POOLS {
1056
    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
1057 1058
    ctx.stack_.back()->set("pd-pools", l);
    ctx.stack_.push_back(l);
1059 1060
    ctx.enter(ctx.PD_POOLS);
} COLON LSQUARE_BRACKET pd_pools_list_content RSQUARE_BRACKET {