/**
 * @file yang.y
 * @author Pavol Vican
 * @brief YANG parser for libyang (bison grammar)
 *
 * Copyright (c) 2015 CESNET, z.s.p.o.
 *
 * This source code is licensed under BSD 3-Clause License (the "License").
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://opensource.org/licenses/BSD-3-Clause
 */

%define api.pure full
%locations

%parse-param {void *scanner}
%parse-param {struct yang_parameter *param}

%lex-param {void *scanner}

%{
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include "libyang.h"
#include "common.h"
#include "context.h"
#include "resolve.h"
#include "parser_yang.h"
#include "parser_yang_lex.h"
#include "parser.h"

#define YANG_ADDELEM(current_ptr, size)                                                  \
    if (size == LY_ARRAY_MAX(size)) {                                                    \
         LOGERR(LY_EINT, "Reached limit (%"PRIu64") for storing typedefs.", LY_ARRAY_MAX(trg->tpdf_size));\
         free(s);                                                                        \
         YYABORT;                                                                        \
    } else if (!(size % LY_YANG_ARRAY_SIZE)) {                                           \
        void *tmp;                                                                       \
                                                                                         \
        tmp = realloc(current_ptr, (sizeof *current_ptr) * (size + LY_YANG_ARRAY_SIZE)); \
        if (!tmp) {                                                                      \
            LOGMEM;                                                                      \
            free(s);                                                                     \
            YYABORT;                                                                     \
        }                                                                                \
        memset(tmp + (sizeof *current_ptr) * size, 0, (sizeof *current_ptr) * LY_YANG_ARRAY_SIZE); \
        current_ptr = tmp;                                                               \
    }                                                                                    \
    actual = &current_ptr[size++];                                                       \

void yyerror(YYLTYPE *yylloc, void *scanner, struct yang_parameter *param, ...);
/* pointer on the current parsed element 'actual' */
%}

%union {
  int32_t i;
  uint32_t uint;
  char *str;
  char **p_str;
  void *v;
  char ch;
  struct yang_type *type;
  struct lys_deviation *dev;
  struct lys_deviate *deviate;
  union {
    uint32_t index;
    struct lys_node_container *container;
    struct lys_node_anydata *anydata;
    struct type_node node;
    struct lys_node_case *cs;
    struct lys_node_grp *grouping;
    struct lys_refine *refine;
    struct lys_node_notif *notif;
    struct lys_node_uses *uses;
    struct lys_node_inout *inout;
    struct lys_node_augment *augment;
  } nodes;
  enum yytokentype token;
  struct {
    void *actual;
    enum yytokentype token;
  } backup_token;
  struct {
    struct lys_revision **revision;
    int index;
  } revisions;
}

%token UNION_KEYWORD
%token ANYXML_KEYWORD
%token WHITESPACE
%token ERROR
%token EOL
%token STRING
%token STRINGS
%token IDENTIFIER
%token IDENTIFIERPREFIX
%token REVISION_DATE
%token TAB
%token DOUBLEDOT
%token URI
%token INTEGER
%token NON_NEGATIVE_INTEGER
%token ZERO
%token DECIMAL
%token ARGUMENT_KEYWORD
%token AUGMENT_KEYWORD
%token BASE_KEYWORD
%token BELONGS_TO_KEYWORD
%token BIT_KEYWORD
%token CASE_KEYWORD
%token CHOICE_KEYWORD
%token CONFIG_KEYWORD
%token CONTACT_KEYWORD
%token CONTAINER_KEYWORD
%token DEFAULT_KEYWORD
%token DESCRIPTION_KEYWORD
%token ENUM_KEYWORD
%token ERROR_APP_TAG_KEYWORD
%token ERROR_MESSAGE_KEYWORD
%token EXTENSION_KEYWORD
%token DEVIATION_KEYWORD
%token DEVIATE_KEYWORD
%token FEATURE_KEYWORD
%token FRACTION_DIGITS_KEYWORD
%token GROUPING_KEYWORD
%token IDENTITY_KEYWORD
%token IF_FEATURE_KEYWORD
%token IMPORT_KEYWORD
%token INCLUDE_KEYWORD
%token INPUT_KEYWORD
%token KEY_KEYWORD
%token LEAF_KEYWORD
%token LEAF_LIST_KEYWORD
%token LENGTH_KEYWORD
%token LIST_KEYWORD
%token MANDATORY_KEYWORD
%token MAX_ELEMENTS_KEYWORD
%token MIN_ELEMENTS_KEYWORD
%token MODULE_KEYWORD
%token MUST_KEYWORD
%token NAMESPACE_KEYWORD
%token NOTIFICATION_KEYWORD
%token ORDERED_BY_KEYWORD
%token ORGANIZATION_KEYWORD
%token OUTPUT_KEYWORD
%token PATH_KEYWORD
%token PATTERN_KEYWORD
%token POSITION_KEYWORD
%token PREFIX_KEYWORD
%token PRESENCE_KEYWORD
%token RANGE_KEYWORD
%token REFERENCE_KEYWORD
%token REFINE_KEYWORD
%token REQUIRE_INSTANCE_KEYWORD
%token REVISION_KEYWORD
%token REVISION_DATE_KEYWORD
%token RPC_KEYWORD
%token STATUS_KEYWORD
%token SUBMODULE_KEYWORD
%token TYPE_KEYWORD
%token TYPEDEF_KEYWORD
%token UNIQUE_KEYWORD
%token UNITS_KEYWORD
%token USES_KEYWORD
%token VALUE_KEYWORD
%token WHEN_KEYWORD
%token YANG_VERSION_KEYWORD
%token YIN_ELEMENT_KEYWORD
%token ADD_KEYWORD
%token CURRENT_KEYWORD
%token DELETE_KEYWORD
%token DEPRECATED_KEYWORD
%token FALSE_KEYWORD
%token NOT_SUPPORTED_KEYWORD
%token OBSOLETE_KEYWORD
%token REPLACE_KEYWORD
%token SYSTEM_KEYWORD
%token TRUE_KEYWORD
%token UNBOUNDED_KEYWORD
%token USER_KEYWORD
%token ACTION_KEYWORD
%token MODIFIER_KEYWORD
%token ANYDATA_KEYWORD
%token NODE
%token NODE_PRINT
%token EXTENSION_INSTANCE
%token SUBMODULE_EXT_KEYWORD

%type <uint> positive_integer_value
%type <uint> non_negative_integer_value
%type <uint> max_value_arg_str
%type <uint> max_value_arg
%type <uint> max_elements_stmt
%type <uint> min_value_arg_str
%type <uint> min_value_arg
%type <uint> min_elements_stmt
%type <uint> fraction_digits_arg_str
%type <uint> fraction_digits_arg
%type <uint> position_value_arg_str
%type <uint> position_value_arg
%type <uint> yin_element_arg_str
%type <uint> yin_element_arg
%type <uint> fraction_digits_stmt
%type <uint> position_stmt
%type <i> value_stmt
%type <i> require_instance_stmt
%type <i> require_instance_arg_str
%type <i> require_instance_arg
%type <i> import_opt_stmt
%type <i> include_opt_stmt
%type <i> module_header_stmt
%type <i> submodule_header_stmt
%type <str> message_opt_stmt
%type <i> status_stmt
%type <i> status_arg_str
%type <i> status_arg
%type <i> config_stmt
%type <i> config_arg_str
%type <i> config_arg
%type <i> mandatory_stmt
%type <i> mandatory_arg_str
%type <i> mandatory_arg
%type <i> ordered_by_stmt
%type <i> ordered_by_arg_str
%type <i> ordered_by_arg
%type <i> integer_value_arg_str
%type <i> value_arg
%type <i> integer_value
%type <i> ext_substatements
%type <str> pattern_arg_str
%type <nodes> container_opt_stmt
%type <nodes> anyxml_opt_stmt
%type <nodes> choice_opt_stmt
%type <nodes> case_opt_stmt
%type <nodes> grouping_opt_stmt
%type <nodes> leaf_opt_stmt
%type <nodes> leaf_list_opt_stmt
%type <nodes> list_opt_stmt
%type <nodes> type_opt_stmt
%type <nodes> uses_opt_stmt
%type <nodes> refine_body_opt_stmts
%type <nodes> augment_opt_stmt
%type <nodes> rpc_opt_stmt
%type <nodes> notification_opt_stmt
%type <dev> deviation_opt_stmt
%type <deviate> deviate_add_opt_stmt
%type <deviate> deviate_delete_opt_stmt
%type <deviate> deviate_replace_opt_stmt
%type <ch> pattern_opt_stmt
%type <ch> pattern_end
%type <ch> modifier_stmt
%type <p_str> tmp_string
%type <str> string_opt_part1
%type <str> semicolom
%type <str> curly_bracket_open
%type <str> unknown_statement2_yang_stmt
%type <str> unknown_statement2_module_stmt
%type <v> type_ext_alloc
%type <v> typedef_ext_alloc
%type <v> iffeature_ext_alloc
%type <v> restriction_ext_alloc
%type <v> when_ext_alloc
%type <revisions> revision_ext_alloc
%type <token> import_arg_str
%type <token> include_arg_str
%type <token> argument_str
%type <token> belongs_to_arg_str
%type <backup_token> revision_arg_stmt
%type <backup_token> grouping_arg_str
%type <backup_token> container_arg_str
%type <backup_token> leaf_arg_str
%type <backup_token> leaf_list_arg_str
%type <backup_token> list_arg_str
%type <backup_token> choice_arg_str
%type <backup_token> case_arg_str
%type <backup_token> anyxml_arg_str
%type <backup_token> anydata_arg_str
%type <backup_token> uses_arg_str
%type <backup_token> uses_augment_arg
%type <backup_token> augment_arg
%type <backup_token> action_arg_str
%type <backup_token> rpc_arg_str
%type <backup_token> input_arg
%type <backup_token> output_arg
%type <backup_token> notification_arg_str
%type <backup_token> extension_arg_str
%type <backup_token> feature_arg_str
%type <backup_token> identity_arg_str
%type <backup_token> if_feature_arg
%type <backup_token> typedef_arg_str
%type <backup_token> type_arg_str
%type <backup_token> length_arg_str
%type <backup_token> pattern_sep
%type <backup_token> range_arg_str
%type <backup_token> union_spec
%type <backup_token> enum_arg_str
%type <backup_token> bit_arg_str
%type <backup_token> when_arg_str
%type <backup_token> must_agr_str
%type <backup_token> refine_arg_str
%type <backup_token> deviation_arg
%type <backup_token> deviate_not_supported
%type <backup_token> deviate_add
%type <backup_token> deviate_delete
%type <backup_token> deviate_replace
%type <backup_token> string_opt

%destructor { free($$); } pattern_arg_str string_opt_part1 semicolom curly_bracket_open
%destructor { free(($$) ? *$$ : NULL); } tmp_string
%destructor { yang_type_free(param->module->ctx, $$); } type_ext_alloc
%destructor { yang_type_free(param->module->ctx, &((struct lys_tpdf *)$$)->type); } typedef_ext_alloc

%initial-action { yylloc.last_column = 0;
                  if (param->flags & EXT_INSTANCE_SUBSTMT) {
                    is_ext_instance = 1;
                    ext_instance = (struct lys_ext_instance_complex *)param->actual_node;
                    ext_name = (char *)param->data_node;
                  } else {
                    is_ext_instance = 0;
                  }
                  yylloc.last_line = is_ext_instance;     /* HACK for flex - return SUBMODULE_KEYWORD or SUBMODULE_EXT_KEYWORD */
                  param->value = &s;
                  param->data_node = (void **)&data_node;
                  param->actual_node = &actual;
                  backup_type = NODE;
                  trg = (param->submodule) ? (struct lys_module *)param->submodule : param->module;
                }

%%

/* to simplify code, store the module/submodule being processed as trg */

start: module_stmt
 |  submodule_stmt
 |  ext_substatements

tmp_string: STRING { if (yyget_text(scanner)[0] == '"') {
                      char *tmp;

                      s = malloc(yyget_leng(scanner) - 1 + 7 * yylval.i);
                      if (!s) {
                        LOGMEM;
                        YYABORT;
                      }
                      if (!(tmp = yang_read_string(yyget_text(scanner) + 1, s, yyget_leng(scanner) - 2, 0, yylloc.first_column))) {
                        YYABORT;
                      }
                      s = tmp;
                    } else {
                      s = calloc(1, yyget_leng(scanner) - 1);
                      if (!s) {
                        LOGMEM;
                        YYABORT;
                      }
                      memcpy(s, yyget_text(scanner) + 1, yyget_leng(scanner) - 2);
                    }
                    $$ = &s;
                  }

string_1: tmp_string optsep string_2


string_2: @EMPTYDIR@
  |  string_2 '+' optsep
     STRING { if (yyget_leng(scanner) > 2) {
                int length_s = strlen(s), length_tmp = yyget_leng(scanner);
                char *tmp;

                tmp = realloc(s, length_s + length_tmp - 1);
                if (!tmp) {
                  LOGMEM;
                  YYABORT;
                }
                s = tmp;
                if (yyget_text(scanner)[0] == '"') {
                  if (!(tmp = yang_read_string(yyget_text(scanner) + 1, s, length_tmp - 2, length_s, yylloc.first_column))) {
                    YYABORT;
                  }
                  s = tmp;
                } else {
                  memcpy(s + length_s, yyget_text(scanner) + 1, length_tmp - 2);
                  s[length_s + length_tmp - 2] = '\0';
                }
              }
            }
     optsep;

module_arg_str: identifier_arg_str { if (param->submodule) {
                                       free(s);
                                       LOGVAL(LYE_INSTMT, LY_VLOG_NONE, NULL, "module");
                                       YYABORT;
                                     }
                                     trg = param->module;
                                     yang_read_common(trg,s,MODULE_KEYWORD);
                                     s = NULL;
                                     actual_type = MODULE_KEYWORD;
                                   }

module_stmt: optsep MODULE_KEYWORD sep module_arg_str
             '{' stmtsep
                 module_header_stmts
                 linkage_stmts
                 meta_stmts
                 revision_stmts
                 body_stmts_end
             '}' optsep


module_header_stmts: module_header_stmt { if (!param->module->ns) {
                                            LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, "namespace", "module");
                                            YYABORT;
                                          }
                                          if (!param->module->prefix) {
                                            LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, "prefix", "module");
                                            YYABORT;
                                          }
                                        }

module_header_stmt: @EMPTYDIR@  { $$ = 0; }
  |  module_header_stmt yang_version_stmt { if (yang_check_version(param->module, param->submodule, s, $1)) {
                                              YYABORT;
                                            }
                                            $$ = 1;
                                            s = NULL;
                                          }
  |  module_header_stmt namespace_stmt { if (yang_read_common(param->module, s, NAMESPACE_KEYWORD)) {
                                           YYABORT;
                                         }
                                         s = NULL;
                                       }
  |  module_header_stmt prefix_stmt { if (yang_read_prefix(trg, NULL, s)) {
                                        YYABORT;
                                      }
                                      s = NULL;
                                    }

submodule_arg_str: identifier_arg_str { if (!param->submodule) {
                                          free(s);
                                          LOGVAL(LYE_SUBMODULE, LY_VLOG_NONE, NULL);
                                          YYABORT;
                                        }
                                        trg = (struct lys_module *)param->submodule;
                                        yang_read_common(trg,s,MODULE_KEYWORD);
                                        s = NULL;
                                        actual_type = SUBMODULE_KEYWORD;
                                      }

submodule_stmt: optsep SUBMODULE_KEYWORD sep submodule_arg_str
                '{' stmtsep
                    submodule_header_stmts
                    linkage_stmts
                    meta_stmts
                    revision_stmts
                    body_stmts_end
                '}' optsep


submodule_header_stmts: submodule_header_stmt { if (!param->submodule->prefix) {
                                                  LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, "belongs-to", "submodule");
                                                  YYABORT;
                                                }
                                                if (!$1) {
                                                  /* check version compatibility with the main module */
                                                  if (param->module->version > 1) {
                                                      LOGVAL(LYE_INVER, LY_VLOG_NONE, NULL);
                                                      YYABORT;
                                                  }
                                                }
                                              }

submodule_header_stmt: @EMPTYDIR@ { $$ = 0; }
  |  submodule_header_stmt yang_version_stmt { if (yang_check_version(param->module, param->submodule, s, $1)) {
                                                 YYABORT;
                                               }
                                               $$ = 1;
                                               s = NULL;
                                             }
  |  submodule_header_stmt belongs_to_stmt stmtsep

yang_version_arg: string { backup_type = actual_type;
                           actual_type = YANG_VERSION_KEYWORD;
                         }

yang_version_stmt: YANG_VERSION_KEYWORD sep yang_version_arg stmtend

namespace_arg_str: string { backup_type = actual_type;
                            actual_type = NAMESPACE_KEYWORD;
                          }

namespace_stmt: NAMESPACE_KEYWORD sep namespace_arg_str stmtend

linkage_stmts: @EMPTYDIR@
 |  linkage_stmts import_stmt stmtsep
 |  linkage_stmts include_stmt stmtsep

import_stmt: IMPORT_KEYWORD sep import_arg_str
             '{' stmtsep
                 import_opt_stmt
             '}' { actual_type = $3;
                   backup_type = NODE;
                   actual = NULL;
                 }

import_arg_str: identifier_arg_str { YANG_ADDELEM(trg->imp, trg->imp_size);
                                     /* HACK for unres */
                                     ((struct lys_import *)actual)->module = (struct lys_module *)s;
                                     s = NULL;
                                     $$ = actual_type;
                                     actual_type = IMPORT_KEYWORD;
                                   }

import_opt_stmt: @EMPTYDIR@ { $$ = 0; }
  |  import_opt_stmt prefix_stmt { if (yang_read_prefix(trg, actual, s)) {
                                     YYABORT;
                                   }
                                   s = NULL;
                                 }
  |  import_opt_stmt description_stmt { if (trg->version != 2) {
                                          LOGVAL(LYE_INSTMT, LY_VLOG_NONE, NULL, "description");
                                          free(s);
                                          YYABORT;
                                        }
                                        if (yang_read_description(trg, actual, s, "import", IMPORT_KEYWORD)) {
                                          YYABORT;
                                        }
                                        s = NULL;
                                        $$ = $1;
                                      }
  |  import_opt_stmt reference_stmt { if (trg->version != 2) {
                                        LOGVAL(LYE_INSTMT, LY_VLOG_NONE, NULL, "reference");
                                        free(s);
                                        YYABORT;
                                      }
                                      if (yang_read_reference(trg, actual, s, "import", IMPORT_KEYWORD)) {
                                        YYABORT;
                                      }
                                      s = NULL;
                                      $$ = $1;
                                    }
  |  import_opt_stmt revision_date_stmt { if ($1) {
                                            LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "revision-date", "import");
                                            free(s);
                                            YYABORT;
                                          }
                                          memcpy(((struct lys_import *)actual)->rev, s, LY_REV_SIZE-1);
                                          free(s);
                                          s = NULL;
                                          $$ = 1;
                                        }

include_arg_str: identifier_arg_str { YANG_ADDELEM(trg->inc, trg->inc_size);
                                     /* HACK for unres */
                                     ((struct lys_include *)actual)->submodule = (struct lys_submodule *)s;
                                     s = NULL;
                                     $$ = actual_type;
                                     actual_type = INCLUDE_KEYWORD;
                                   }

include_stmt: INCLUDE_KEYWORD sep include_arg_str include_end { actual_type = $3;
                                                                backup_type = NODE;
                                                                actual = NULL;
                                                              }

include_end: ';'
  | '{' stmtsep
        include_opt_stmt
    '}'

include_opt_stmt: @EMPTYDIR@ { $$ = 0; }
  |  include_opt_stmt description_stmt { if (trg->version != 2) {
                                           LOGVAL(LYE_INSTMT, LY_VLOG_NONE, NULL, "description");
                                           free(s);
                                           YYABORT;
                                         }
                                         if (yang_read_description(trg, actual, s, "include", INCLUDE_KEYWORD)) {
                                            YYABORT;
                                         }
                                         s = NULL;
                                         $$ = $1;
                                       }
  |  include_opt_stmt reference_stmt { if (trg->version != 2) {
                                         LOGVAL(LYE_INSTMT, LY_VLOG_NONE, NULL, "reference");
                                         free(s);
                                         YYABORT;
                                       }
                                       if (yang_read_reference(trg, actual, s, "include", INCLUDE_KEYWORD)) {
                                         YYABORT;
                                       }
                                       s = NULL;
                                       $$ = $1;
                                     }
  |  include_opt_stmt revision_date_stmt { if ($1) {
                                             LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "revision-date", "include");
                                             free(s);
                                             YYABORT;
                                           }
                                           memcpy(((struct lys_include *)actual)->rev, s, LY_REV_SIZE-1);
                                           free(s);
                                           s = NULL;
                                           $$ = 1;
                                         }

revision_date_arg: date_arg_str { backup_type = actual_type;
                                  actual_type = REVISION_DATE_KEYWORD;
                                }

revision_date_stmt: REVISION_DATE_KEYWORD sep revision_date_arg stmtend

belongs_to_arg_str: identifier_arg_str { $$ = actual_type;
                                         if (is_ext_instance) {
                                           if (yang_read_extcomplex_str(trg, ext_instance, "belongs-to", ext_name, s,
                                                                        0, LY_STMT_BELONGSTO)) {
                                             YYABORT;
                                           }
                                         } else {
                                           if (param->submodule->prefix) {
                                             LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "belongs-to", "submodule");
                                             free(s);
                                             YYABORT;
                                           }
                                           if (!ly_strequal(s, param->submodule->belongsto->name, 0)) {
                                             LOGVAL(LYE_INARG, LY_VLOG_NONE, NULL, s, "belongs-to");
                                             free(s);
                                             YYABORT;
                                           }
                                           free(s);
                                         }
                                         s = NULL;
                                         actual_type = BELONGS_TO_KEYWORD;
                                       }

belongs_to_stmt: BELONGS_TO_KEYWORD sep belongs_to_arg_str
                 '{' stmtsep
                     prefix_stmt
                 '}' { if (is_ext_instance) {
                         if (yang_read_extcomplex_str(trg, ext_instance, "prefix", "belongs-to", s,
                                                      LY_STMT_BELONGSTO, LY_STMT_PREFIX)) {
                           YYABORT;
                         }
                       } else {
                         if (yang_read_prefix(trg, NULL, s)) {
                           YYABORT;
                         }
                       }
                       s = NULL;
                       actual_type = $3;
                     }

prefix_arg: prefix_arg_str { backup_type = actual_type;
                             actual_type = PREFIX_KEYWORD;
                           }

prefix_stmt: PREFIX_KEYWORD sep prefix_arg stmtend

meta_stmts: @EMPTYDIR@
  |  meta_stmts organization_stmt { if (yang_read_common(trg, s, ORGANIZATION_KEYWORD)) {
                                      YYABORT;
                                    }
                                    s = NULL;
                                  }
  |  meta_stmts contact_stmt { if (yang_read_common(trg, s, CONTACT_KEYWORD)) {
                                 YYABORT;
                               }
                               s = NULL;
                             }
  |  meta_stmts description_stmt { if (yang_read_description(trg, NULL, s, NULL, MODULE_KEYWORD)) {
                                     YYABORT;
                                   }
                                   s = NULL;
                                 }
  |  meta_stmts reference_stmt { if (yang_read_reference(trg, NULL, s, NULL, MODULE_KEYWORD)) {
                                   YYABORT;
                                 }
                                 s=NULL;
                               }

organization_arg: string { backup_type = actual_type;
                           actual_type = ORGANIZATION_KEYWORD;
                         }

organization_stmt: ORGANIZATION_KEYWORD sep organization_arg stmtend

contact_arg: string { backup_type = actual_type;
                      actual_type = CONTACT_KEYWORD;
                    }

contact_stmt: CONTACT_KEYWORD sep contact_arg stmtend

description_arg: string { backup_type = actual_type;
                          actual_type = DESCRIPTION_KEYWORD;
                        }

description_stmt: DESCRIPTION_KEYWORD sep description_arg stmtend

reference_arg: string { backup_type = actual_type;
                        actual_type = REFERENCE_KEYWORD;
                      }

reference_stmt: REFERENCE_KEYWORD sep reference_arg stmtend

revision_stmts: revision_stmts_opt { if (trg->rev_size) {
                                      struct lys_revision *tmp;

                                      tmp = realloc(trg->rev, trg->rev_size * sizeof *trg->rev);
                                      if (!tmp) {
                                        LOGMEM;
                                        YYABORT;
                                      }
                                      trg->rev = tmp;
                                    }
                                  }


revision_arg_stmt: date_arg_str { $$.token = actual_type;
                                  $$.actual = actual;
                                  if (!is_ext_instance) {
                                    YANG_ADDELEM(trg->rev, trg->rev_size);
                                  }
                                  memcpy(((struct lys_revision *)actual)->date, s, LY_REV_SIZE);
                                  free(s);
                                  s = NULL;
                                  actual_type = REVISION_KEYWORD;
                                }

revision_stmts_opt: @EMPTYDIR@
  |  revision_stmts_opt revision_stmt stmtsep { int i;

                                                /* check uniqueness of the revision date - not required by RFC */
                                                for (i = 0; i < (trg->rev_size - 1); i++) {
                                                  if (!strcmp(trg->rev[i].date, trg->rev[trg->rev_size - 1].date)) {
                                                    LOGWRN("Module's revisions are not unique (%s).", trg->rev[trg->rev_size - 1].date);
                                                    break;
                                                  }
                                                }
                                              }

revision_stmt: REVISION_KEYWORD sep revision_arg_stmt revision_end { actual_type = $3.token;
                                                                     actual = $3.actual;
                                                                   }

revision_end: ';'
  | '{' stmtsep
        revision_opt_stmt
    '}'

revision_opt_stmt: @EMPTYDIR@
  |  revision_opt_stmt description_stmt { if (yang_read_description(trg, actual, s, "revision",REVISION_KEYWORD)) {
                                            YYABORT;
                                          }
                                          s = NULL;
                                        }
  |  revision_opt_stmt reference_stmt { if (yang_read_reference(trg, actual, s, "revision", REVISION_KEYWORD)) {
                                          YYABORT;
                                        }
                                        s = NULL;
                                      }
  ;

date_arg_str: REVISION_DATE { s = strdup(yyget_text(scanner));
                              if (!s) {
                                LOGMEM;
                                YYABORT;
                              }
                            }
              optsep
  | string_1 { if (lyp_check_date(s)) {
                   free(s);
                   YYABORT;
               }
             }

body_stmts_end: body_stmts { void *tmp;

                             if (trg->tpdf_size) {
                               tmp = realloc(trg->tpdf, trg->tpdf_size * sizeof *trg->tpdf);
                               if (!tmp) {
                                 LOGMEM;
                                 YYABORT;
                               }
                               trg->tpdf = tmp;
                             }

                             if (trg->features_size) {
                               tmp = realloc(trg->features, trg->features_size * sizeof *trg->features);
                               if (!tmp) {
                                 LOGMEM;
                                 YYABORT;
                               }
                               trg->features = tmp;
                             }

                             if (trg->ident_size) {
                               tmp = realloc(trg->ident, trg->ident_size * sizeof *trg->ident);
                               if (!tmp) {
                                 LOGMEM;
                                 YYABORT;
                               }
                               trg->ident = tmp;
                             }

                             if (trg->augment_size) {
                               tmp = realloc(trg->augment, trg->augment_size * sizeof *trg->augment);
                               if (!tmp) {
                                 LOGMEM;
                                 YYABORT;
                               }
                               trg->augment = tmp;
                             }

                             if (trg->extensions_size) {
                               tmp = realloc(trg->extensions, trg->extensions_size * sizeof *trg->extensions);
                               if (!tmp) {
                                 LOGMEM;
                                 YYABORT;
                               }
                               trg->extensions = tmp;
                             }
                           }

body_stmts: @EMPTYDIR@ { /* check the module with respect to the context now */
                         if (!param->submodule) {
                           switch (lyp_ctx_check_module(trg)) {
                           case -1:
                             YYABORT;
                           case 0:
                             break;
                           case 1:
                             /* it's already there */
                             param->flags |= YANG_EXIST_MODULE;
                             YYABORT;
                           }
                         }
                         param->flags &= (~YANG_REMOVE_IMPORT);
                         if (yang_check_imports(trg, param->unres)) {
                           YYABORT;
                         }
                         actual = NULL;
                       }
  | body_stmts body_stmt stmtsep { actual = NULL; }


body_stmt: extension_stmt
  | feature_stmt
  | identity_stmt
  | typedef_stmt
  | grouping_stmt
  | data_def_stmt
  | augment_stmt
  | rpc_stmt
  | notification_stmt
  | deviation_stmt


extension_arg_str: identifier_arg_str { $$.token = actual_type;
                                        $$.actual = actual;
                                        YANG_ADDELEM(trg->extensions, trg->extensions_size);
                                        trg->extensions_size--;
                                        ((struct lys_ext *)actual)->name = lydict_insert_zc(param->module->ctx, s);
                                        ((struct lys_ext *)actual)->module = trg;
                                        if (lyp_check_identifier(((struct lys_ext *)actual)->name, LY_IDENT_EXTENSION, trg, NULL)) {
                                          trg->extensions_size++;
                                          YYABORT;
                                        }
                                        trg->extensions_size++;
                                        s = NULL;
                                        actual_type = EXTENSION_KEYWORD;
                                      }

extension_stmt: EXTENSION_KEYWORD sep extension_arg_str extension_end
                { struct lys_ext *ext = actual;
                  ext->plugin = ext_get_plugin(ext->name, ext->module->name, ext->module->rev ? ext->module->rev[0].date : NULL);
                  actual_type = $3.token;
                  actual = $3.actual;
                }

extension_end: ';'
  | '{' stmtsep
        extension_opt_stmt
    '}'

extension_opt_stmt: @EMPTYDIR@
  |  extension_opt_stmt argument_stmt stmtsep
  |  extension_opt_stmt status_stmt { if (((struct lys_ext *)actual)->flags & LYS_STATUS_MASK) {
                                        LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "status", "extension");
                                        YYABORT;
                                      }
                                      ((struct lys_ext *)actual)->flags |= $2;
                                    }
  |  extension_opt_stmt description_stmt { if (yang_read_description(trg, actual, s, "extension", NODE)) {
                                             YYABORT;
                                           }
                                           s = NULL;
                                         }
  |  extension_opt_stmt reference_stmt { if (yang_read_reference(trg, actual, s, "extension", NODE)) {
                                           YYABORT;
                                         }
                                         s = NULL;
                                       }

argument_str: identifier_arg_str { $$ = actual_type;
                                   if (is_ext_instance) {
                                     if (yang_read_extcomplex_str(trg, ext_instance, "argument", ext_name, s,
                                                                  0, LY_STMT_ARGUMENT)) {
                                       YYABORT;
                                     }
                                   } else {
                                     if (((struct lys_ext *)actual)->argument) {
                                        LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "argument", "extension");
                                        free(s);
                                        YYABORT;
                                     }
                                     ((struct lys_ext *)actual)->argument = lydict_insert_zc(param->module->ctx, s);
                                   }
                                   s = NULL;
                                   actual_type = ARGUMENT_KEYWORD;
                                 }

argument_stmt: ARGUMENT_KEYWORD sep  argument_str argument_end { actual_type = $3; }

argument_end: ';'
  | '{' stmtsep
        yin_element_stmt
    '}'

yin_element_arg: yin_element_arg_str { $$ = $1;
                                       backup_type = actual_type;
                                       actual_type = YIN_ELEMENT_KEYWORD;
                                     }

yin_element_stmt: @EMPTYDIR@
  |  YIN_ELEMENT_KEYWORD sep yin_element_arg stmtend
     { if (is_ext_instance) {
         int c;
         const char ***p;
         uint8_t *val;
         struct lyext_substmt *info;

         c = 0;
         p = lys_ext_complex_get_substmt(LY_STMT_ARGUMENT, ext_instance, &info);
         if (info->cardinality >= LY_STMT_CARD_SOME) {
           /* get the index in the array to add new item */
           for (c = 0; p[0][c + 1]; c++);
           val = (uint8_t *)p[1];
         } else {
           val = (uint8_t *)(p + 1);
         }
         val[c] = ($3 == LYS_YINELEM) ? 1 : 2;
       } else {
         ((struct lys_ext *)actual)->flags |= $3;
       }
     }

yin_element_arg_str: TRUE_KEYWORD optsep { $$ = LYS_YINELEM; }
  | FALSE_KEYWORD optsep { $$ = 0; }
  | string_1 { if (!strcmp(s, "true")) {
                 $$ = LYS_YINELEM;
               } else if (!strcmp(s, "false")) {
                 $$ = 0;
               } else {
                 LOGVAL(LYE_INSTMT, LY_VLOG_NONE, NULL, s);
                 free(s);
                 YYABORT;
               }
               free(s);
               s = NULL;
             }

status_arg: status_arg_str { $$ = $1;
                             backup_type = actual_type;
                             actual_type = STATUS_KEYWORD;
                           }

status_stmt:  STATUS_KEYWORD sep status_arg stmtend { $$ = $3; }

status_arg_str: CURRENT_KEYWORD optsep { $$ = LYS_STATUS_CURR; }
  | OBSOLETE_KEYWORD optsep { $$ = LYS_STATUS_OBSLT; }
  | DEPRECATED_KEYWORD optsep { $$ = LYS_STATUS_DEPRC; }
  | string_1 { if (!strcmp(s, "current")) {
                 $$ = LYS_STATUS_CURR;
               } else if (!strcmp(s, "obsolete")) {
                 $$ = LYS_STATUS_OBSLT;
               } else if (!strcmp(s, "deprecated")) {
                 $$ = LYS_STATUS_DEPRC;
               } else {
                 LOGVAL(LYE_INSTMT, LY_VLOG_NONE, NULL, s);
                 free(s);
                 YYABORT;
               }
               free(s);
               s = NULL;
             }

feature_arg_str: identifier_arg_str { /* check uniqueness of feature's names */
                                      if (lyp_check_identifier(s, LY_IDENT_FEATURE, trg, NULL)) {
                                        free(s);
                                        YYABORT;
                                      }
                                      $$.token = actual_type;
                                      $$.actual = actual;
                                      YANG_ADDELEM(trg->features, trg->features_size);
                                      ((struct lys_feature *)actual)->name = lydict_insert_zc(trg->ctx, s);
                                      ((struct lys_feature *)actual)->module = trg;
                                      s = NULL;
                                      actual_type = FEATURE_KEYWORD;
                                    }

feature_stmt: FEATURE_KEYWORD sep feature_arg_str feature_end
              { actual = $3.actual;
                actual_type = $3.token;
              }

feature_end: ';'
  | '{' stmtsep
        feature_opt_stmt
    '}' { struct lys_iffeature *tmp;

          if (((struct lys_feature *)actual)->iffeature_size) {
            tmp = realloc(((struct lys_feature *)actual)->iffeature,
                          ((struct lys_feature *)actual)->iffeature_size * sizeof *tmp);
            if (!tmp) {
              LOGMEM;
              YYABORT;
            }
            ((struct lys_feature *)actual)->iffeature = tmp;
          }
        }

feature_opt_stmt: @EMPTYDIR@
  |  feature_opt_stmt if_feature_stmt stmtsep
  |  feature_opt_stmt status_stmt { if (((struct lys_feature *)actual)->flags & LYS_STATUS_MASK) {
                                      LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "status", "feature");
                                      YYABORT;
                                    }
                                    ((struct lys_feature *)actual)->flags |= $2;
                                  }
  |  feature_opt_stmt description_stmt { if (yang_read_description(trg, actual, s, "feature", NODE)) {
                                           YYABORT;
                                         }
                                         s = NULL;
                                       }
  |  feature_opt_stmt reference_stmt { if (yang_read_reference(trg, actual, s, "feature", NODE)) {
                                         YYABORT;
                                       }
                                       s = NULL;
                                     }

if_feature_arg: string { $$.token = actual_type;
                         $$.actual = actual;
                         switch (actual_type) {
                         case FEATURE_KEYWORD:
                           YANG_ADDELEM(((struct lys_feature *)actual)->iffeature,
                                        ((struct lys_feature *)actual)->iffeature_size);
                           break;
                         case IDENTITY_KEYWORD:
                           if (trg->version < 2) {
                             LOGVAL(LYE_INSTMT, LY_VLOG_NONE, NULL, "if-feature", "identity");
                             free(s);
                             YYABORT;
                           }
                           YANG_ADDELEM(((struct lys_ident *)actual)->iffeature,
                                        ((struct lys_ident *)actual)->iffeature_size);
                           break;
                         case ENUM_KEYWORD:
                           if (trg->version < 2) {
                             LOGVAL(LYE_INSTMT, LY_VLOG_NONE, NULL, "if-feature");
                             free(s);
                             YYABORT;
                           }
                           YANG_ADDELEM(((struct lys_type_enum *)actual)->iffeature,
                                        ((struct lys_type_enum *)actual)->iffeature_size);
                           break;
                         case BIT_KEYWORD:
                           if (trg->version < 2) {
                             LOGVAL(LYE_INSTMT, LY_VLOG_NONE, NULL, "if-feature", "bit");
                             free(s);
                             YYABORT;
                           }
                           YANG_ADDELEM(((struct lys_type_bit *)actual)->iffeature,
                                        ((struct lys_type_bit *)actual)->iffeature_size);
                           break;
                         case REFINE_KEYWORD:
                           if (trg->version < 2) {
                             LOGVAL(LYE_INSTMT, LY_VLOG_NONE, NULL, "if-feature");
                             free(s);
                             YYABORT;
                           }
                           YANG_ADDELEM(((struct lys_refine *)actual)->iffeature,
                                        ((struct lys_refine *)actual)->iffeature_size);
                           break;
                         case EXTENSION_INSTANCE:
                           /* nothing change */
                           break;
                         default:
                           /* lys_node_* */
                           YANG_ADDELEM(((struct lys_node *)actual)->iffeature,
                                        ((struct lys_node *)actual)->iffeature_size);
                           break;
                         }
                         ((struct lys_iffeature *)actual)->features = (struct lys_feature **)s;
                         s = NULL;
                         actual_type = IF_FEATURE_KEYWORD;
                       }

if_feature_stmt: IF_FEATURE_KEYWORD sep if_feature_arg if_feature_end
                 { actual = $3.actual;
                   actual_type = $3.token;
                 }

if_feature_end: ';'
  | '{' stmtsep '}'

identity_arg_str: identifier_arg_str { const char *tmp;

                                       tmp = lydict_insert_zc(trg->ctx, s);
                                       s = NULL;
                                       if (dup_identities_check(tmp, trg)) {
                                         lydict_remove(trg->ctx, tmp);
                                         YYABORT;
                                       }
                                       $$.token = actual_type;
                                       $$.actual = actual;
                                       YANG_ADDELEM(trg->ident, trg->ident_size);
                                       ((struct lys_ident *)actual)->name = tmp;
                                       ((struct lys_ident *)actual)->module = trg;
                                       actual_type = IDENTITY_KEYWORD;
                                     }

identity_stmt: IDENTITY_KEYWORD sep identity_arg_str identity_end
               { actual = $3.actual;
                 actual_type = $3.token;
               }

identity_end: ';'
  |  '{' stmtsep
         identity_opt_stmt
     '}' { void *tmp;

           if (((struct lys_ident *)actual)->base_size) {
             tmp = realloc(((struct lys_ident *)actual)->base,
                           ((struct lys_ident *)actual)->base_size * sizeof *((struct lys_ident *)actual)->base);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             ((struct lys_ident *)actual)->base = tmp;
           }

           if (((struct lys_ident *)actual)->iffeature_size) {
             tmp = realloc(((struct lys_ident *)actual)->iffeature,
                           ((struct lys_ident *)actual)->iffeature_size * sizeof *((struct lys_ident *)actual)->iffeature);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             ((struct lys_ident *)actual)->iffeature = tmp;
           }
         }

identity_opt_stmt: @EMPTYDIR@
  |  identity_opt_stmt base_stmt { void *identity;

                                   if ((trg->version < 2) && ((struct lys_ident *)actual)->base_size) {
                                     free(s);
                                     LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "base", "identity");
                                     YYABORT;
                                   }
                                   identity = actual;
                                   YANG_ADDELEM(((struct lys_ident *)actual)->base,
                                                ((struct lys_ident *)actual)->base_size);
                                   *((struct lys_ident **)actual) = (struct lys_ident *)s;
                                   s = NULL;
                                   actual = identity;
                                 }
  |  identity_opt_stmt if_feature_stmt stmtsep
  |  identity_opt_stmt status_stmt { if (((struct lys_ident *)actual)->flags & LYS_STATUS_MASK) {
                                       LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "status", "identity");
                                       YYABORT;
                                     }
                                     ((struct lys_ident *)actual)->flags |= $2;
                                   }
  |  identity_opt_stmt description_stmt { if (yang_read_description(trg, actual, s, "identity", NODE)) {
                                            YYABORT;
                                          }
                                          s = NULL;
                                        }
  |  identity_opt_stmt reference_stmt { if (yang_read_reference(trg, actual, s, "identity", NODE)) {
                                          YYABORT;
                                        }
                                        s = NULL;
                                      }

base_arg: identifier_ref_arg_str { backup_type = actual_type;
                                   actual_type = BASE_KEYWORD;
                                 }

base_stmt: BASE_KEYWORD sep base_arg stmtend

typedef_arg_str: identifier_arg_str { tpdf_parent = (actual_type == EXTENSION_INSTANCE) ? ext_instance : actual;
                                      $$.token = actual_type;
                                      $$.actual = actual;
                                      if (lyp_check_identifier(s, LY_IDENT_TYPE, trg, tpdf_parent)) {
                                        free(s);
                                        YYABORT;
                                      }
                                      switch (actual_type) {
                                      case MODULE_KEYWORD:
                                      case SUBMODULE_KEYWORD:
                                        YANG_ADDELEM(trg->tpdf, trg->tpdf_size);
                                        break;
                                      case GROUPING_KEYWORD:
                                        YANG_ADDELEM(((struct lys_node_grp *)tpdf_parent)->tpdf,
                                                     ((struct lys_node_grp *)tpdf_parent)->tpdf_size);
                                        break;
                                      case CONTAINER_KEYWORD:
                                        YANG_ADDELEM(((struct lys_node_container *)tpdf_parent)->tpdf,
                                                     ((struct lys_node_container *)tpdf_parent)->tpdf_size);
                                        break;
                                      case LIST_KEYWORD:
                                        YANG_ADDELEM(((struct lys_node_list *)tpdf_parent)->tpdf,
                                                     ((struct lys_node_list *)tpdf_parent)->tpdf_size);
                                        break;
                                      case RPC_KEYWORD:
                                      case ACTION_KEYWORD:
                                        YANG_ADDELEM(((struct lys_node_rpc_action *)tpdf_parent)->tpdf,
                                                     ((struct lys_node_rpc_action *)tpdf_parent)->tpdf_size);
                                        break;
                                      case INPUT_KEYWORD:
                                      case OUTPUT_KEYWORD:
                                        YANG_ADDELEM(((struct lys_node_inout *)tpdf_parent)->tpdf,
                                                     ((struct lys_node_inout *)tpdf_parent)->tpdf_size);
                                        break;
                                      case NOTIFICATION_KEYWORD:
                                        YANG_ADDELEM(((struct lys_node_notif *)tpdf_parent)->tpdf,
                                                     ((struct lys_node_notif *)tpdf_parent)->tpdf_size);
                                        break;
                                      case EXTENSION_INSTANCE:
                                        /* typedef is already allocated */
                                        break;
                                      default:
                                        /* another type of nodetype is error*/
                                        LOGINT;
                                        free(s);
                                        YYABORT;
                                      }
                                      ((struct lys_tpdf *)actual)->name = lydict_insert_zc(param->module->ctx, s);
                                      ((struct lys_tpdf *)actual)->module = trg;
                                      s = NULL;
                                      actual_type = TYPEDEF_KEYWORD;
                                    }

typedef_stmt: TYPEDEF_KEYWORD sep typedef_arg_str
              '{' stmtsep
                  type_opt_stmt
              '}' { if (!($6.node.flag & LYS_TYPE_DEF)) {
                      LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, "type", "typedef");
                      YYABORT;
                    }
                    actual_type = $3.token;
                    actual = $3.actual;
                  }

type_opt_stmt: @EMPTYDIR@ { $$.node.ptr_tpdf = actual;
                            $$.node.flag = 0;
                          }
  |  type_opt_stmt type_stmt stmtsep { $1.node.flag |= LYS_TYPE_DEF;
                                       $$ = $1;
                                     }
  |  type_opt_stmt units_stmt { if (yang_read_units(trg, $1.node.ptr_tpdf, s, TYPEDEF_KEYWORD)) {
                                  YYABORT;
                                }
                                s = NULL;
                              }
  |  type_opt_stmt default_stmt { if (yang_read_default(trg, $1.node.ptr_tpdf, s, TYPEDEF_KEYWORD)) {
                                    YYABORT;
                                  }
                                  s = NULL;
                                }
  |  type_opt_stmt status_stmt { if ($1.node.ptr_tpdf->flags & LYS_STATUS_MASK) {
                                   LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "status", "typedef");
                                   YYABORT;
                                 }
                                 $1.node.ptr_tpdf->flags |= $2;
                               }
  |  type_opt_stmt description_stmt { if (yang_read_description(trg, $1.node.ptr_tpdf, s, "typedef", NODE)) {
                                        YYABORT;
                                      }
                                      s = NULL;
                                    }
  |  type_opt_stmt reference_stmt { if (yang_read_reference(trg, $1.node.ptr_tpdf, s, "typedef", NODE)) {
                                      YYABORT;
                                    }
                                    s = NULL;
                                  }

type_stmt: TYPE_KEYWORD sep type_arg_str type_end
           { actual_type = $3.token;
             actual = $3.actual;
           }

type_arg_str: identifier_ref_arg_str { $$.token = actual_type;
                                       $$.actual = actual;
                                       if (!(actual = yang_read_type(trg, actual, s, actual_type))) {
                                         YYABORT;
                                       }
                                       s = NULL;
                                       actual_type = TYPE_KEYWORD;
                                     }

type_end: ';'
  |  '{' stmtsep
         type_body_stmts
      '}'

type_body_stmts: some_restrictions { if (((struct yang_type *)actual)->base == LY_TYPE_STRING &&
                                         ((struct yang_type *)actual)->type->info.str.pat_count) {
                                       void *tmp;

                                       tmp = realloc(((struct yang_type *)actual)->type->info.str.patterns,
                                                     ((struct yang_type *)actual)->type->info.str.pat_count * sizeof *((struct yang_type *)actual)->type->info.str.patterns);
                                       if (!tmp) {
                                         LOGMEM;
                                         YYABORT;
                                       }
                                       ((struct yang_type *)actual)->type->info.str.patterns = tmp;

#ifdef LY_ENABLED_CACHE
                                       if (!(trg->ctx->models.flags & LY_CTX_TRUSTED) && ((struct yang_type *)actual)->type->info.str.patterns_pcre) {
                                         tmp = realloc(((struct yang_type *)actual)->type->info.str.patterns_pcre,
                                                       2 * ((struct yang_type *)actual)->type->info.str.pat_count * sizeof *((struct yang_type *)actual)->type->info.str.patterns_pcre);
                                         if (!tmp) {
                                           LOGMEM;
                                           YYABORT;
                                         }
                                         ((struct yang_type *)actual)->type->info.str.patterns_pcre = tmp;
                                       }
#endif
                                     }
                                     if (((struct yang_type *)actual)->base == LY_TYPE_UNION) {
                                       struct lys_type *tmp;

                                       tmp = realloc(((struct yang_type *)actual)->type->info.uni.types,
                                                     ((struct yang_type *)actual)->type->info.uni.count * sizeof *tmp);
                                       if (!tmp) {
                                         LOGMEM;
                                         YYABORT;
                                       }
                                       ((struct yang_type *)actual)->type->info.uni.types = tmp;
                                     }
                                     if (((struct yang_type *)actual)->base == LY_TYPE_IDENT) {
                                       struct lys_ident **tmp;

                                       tmp = realloc(((struct yang_type *)actual)->type->info.ident.ref,
                                                     ((struct yang_type *)actual)->type->info.ident.count* sizeof *tmp);
                                       if (!tmp) {
                                         LOGMEM;
                                         YYABORT;
                                       }
                                       ((struct yang_type *)actual)->type->info.ident.ref = tmp;
                                     }
                                   }
  | enum_specification
  | bits_specification

some_restrictions: @EMPTYDIR@
  |  some_restrictions require_instance_stmt { if (yang_read_require_instance(actual, $2)) {
                                                 YYABORT;
                                               }
                                             }
  |  some_restrictions path_stmt { /* leafref_specification */
                                   if (yang_read_leafref_path(trg, actual, s)) {
                                     YYABORT;
                                   }
                                   s = NULL;
                                 }
  |  some_restrictions base_stmt { /* identityref_specification */
                                   if (((struct yang_type *)actual)->base && ((struct yang_type *)actual)->base != LY_TYPE_IDENT) {
                                     LOGVAL(LYE_INSTMT, LY_VLOG_NONE, NULL, "base");
                                     return EXIT_FAILURE;
                                   }
                                   ((struct yang_type *)actual)->base = LY_TYPE_IDENT;
                                   yang_type = actual;
                                   YANG_ADDELEM(((struct yang_type *)actual)->type->info.ident.ref,
                                                ((struct yang_type *)actual)->type->info.ident.count);
                                   *((struct lys_ident **)actual) = (struct lys_ident *)s;
                                   actual = yang_type;
                                   s = NULL;
                                 }
  |  some_restrictions length_stmt stmtsep
  |  some_restrictions pattern_stmt stmtsep
  |  some_restrictions fraction_digits_stmt { if (yang_read_fraction(actual, $2)) {
                                                YYABORT;
                                              }
                                            }
  |  some_restrictions range_stmt stmtsep
  |  some_restrictions union_stmt stmtsep

union_stmt: union_spec type_stmt { actual_type = $1.token;
                                   actual = $1.actual;
                                 }

union_spec: @EMPTYDIR@ { struct yang_type *stype = (struct yang_type *)actual;

                         $$.token = actual_type;
                         $$.actual = actual;
                         if (stype->base != 0 && stype->base != LY_TYPE_UNION) {
                           LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "Unexpected type statement.");
                           YYABORT;
                         }
                         stype->base = LY_TYPE_UNION;
                         if (strcmp(stype->name, "union")) {
                           /* type can be a substatement only in "union" type, not in derived types */
                           LOGVAL(LYE_INCHILDSTMT, LY_VLOG_NONE, NULL, "type", "derived type");
                           YYABORT;
                         }
                         YANG_ADDELEM(stype->type->info.uni.types, stype->type->info.uni.count)
                         actual_type = UNION_KEYWORD;
                       }

fraction_digits_arg: fraction_digits_arg_str { $$ = $1;
                                               backup_type = actual_type;
                                               actual_type = FRACTION_DIGITS_KEYWORD;
                                             }

fraction_digits_stmt: FRACTION_DIGITS_KEYWORD sep fraction_digits_arg stmtend { $$ = $3; }

fraction_digits_arg_str: positive_integer_value optsep { $$ = $1; }
  | string_1 { char *endptr = NULL;
               unsigned long val;
               errno = 0;

               val = strtoul(s, &endptr, 10);
               if (*endptr || s[0] == '-' || errno || val == 0 || val > UINT32_MAX) {
                 LOGVAL(LYE_INARG, LY_VLOG_NONE, NULL, s, "fraction-digits");
                 free(s);
                 s = NULL;
                 YYABORT;
               }
               $$ = (uint32_t) val;
               free(s);
               s =NULL;
             }

length_stmt: LENGTH_KEYWORD sep length_arg_str length_end
             { actual = $3.actual;
               actual_type = $3.token;
             }

length_arg_str: string { $$.token = actual_type;
                         $$.actual = actual;
                         if (!(actual = yang_read_length(trg, actual, s, is_ext_instance))) {
                           YYABORT;
                         }
                         actual_type = LENGTH_KEYWORD;
                         s = NULL;
                       }

length_end: ';'
  |  '{' stmtsep
         message_opt_stmt
      '}'

message_opt_stmt: @EMPTYDIR@ { switch (actual_type) {
                               case MUST_KEYWORD:
                                 $$ = "must";
                                 break;
                               case LENGTH_KEYWORD:
                                 $$ = "length";
                                 break;
                               case RANGE_KEYWORD:
                                 $$ = "range";
                                 break;
                               default:
                                 LOGINT;
                                 YYABORT;
                                 break;
                               }
                             }
  |  message_opt_stmt error_message_stmt { if (yang_read_message(trg, actual, s, $1, ERROR_MESSAGE_KEYWORD)) {
                                             YYABORT;
                                           }
                                           s = NULL;
                                         }
  |  message_opt_stmt error_app_tag_stmt { if (yang_read_message(trg, actual, s, $1, ERROR_APP_TAG_KEYWORD)) {
                                             YYABORT;
                                           }
                                           s = NULL;
                                         }
  |  message_opt_stmt description_stmt { if (yang_read_description(trg, actual, s, $1, NODE)) {
                                           YYABORT;
                                          }
                                          s = NULL;
                                        }
  |  message_opt_stmt reference_stmt { if (yang_read_reference(trg, actual, s, $1, NODE)) {
                                         YYABORT;
                                       }
                                       s = NULL;
                                     }

pattern_sep: sep { $$.token = actual_type;
                   $$.actual = actual;
                 }

pattern_stmt: PATTERN_KEYWORD pattern_sep pattern_arg_str pattern_end  {struct lys_restr *pattern = actual;
                                                                        actual = NULL;
#ifdef LY_ENABLED_CACHE
                                                                        if ($2.token != EXTENSION_INSTANCE &&
                                                                            !(data_node && data_node->nodetype != LYS_GROUPING && lys_ingrouping(data_node))) {
                                                                          unsigned int c = 2 * (((struct yang_type *)$2.actual)->type->info.str.pat_count - 1);
                                                                          YANG_ADDELEM(((struct yang_type *)$2.actual)->type->info.str.patterns_pcre, c);
                                                                        }
#endif
                                                                        if (yang_read_pattern(trg, pattern, actual, $3, $4)) {
                                                                          YYABORT;
                                                                        }
                                                                        actual_type = $2.token;
                                                                        actual = $2.actual;
                                                                      }

pattern_arg_str: string { if (actual_type != EXTENSION_INSTANCE) {
                            if (((struct yang_type *)actual)->base != 0 && ((struct yang_type *)actual)->base != LY_TYPE_STRING) {
                              free(s);
                              LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "Unexpected pattern statement.");
                              YYABORT;
                            }
                            ((struct yang_type *)actual)->base = LY_TYPE_STRING;
                            YANG_ADDELEM(((struct yang_type *)actual)->type->info.str.patterns,
                                         ((struct yang_type *)actual)->type->info.str.pat_count);
                          }
                          $$ = s;
                          s = NULL;
                          actual_type = PATTERN_KEYWORD;
                        }

pattern_end: ';' { $$ = 0x06; }
  |  '{' stmtsep
         pattern_opt_stmt
     '}' { $$ = $3; }

pattern_opt_stmt: @EMPTYDIR@ { $$ = 0x06; /* ACK */ }
  |  pattern_opt_stmt modifier_stmt { if (trg->version < 2) {
                                        LOGVAL(LYE_INSTMT, LY_VLOG_NONE, NULL, "modifier");
                                        YYABORT;
                                      }
                                      if ($1 != 0x06) {
                                        LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "modifier", "pattern");
                                        YYABORT;
                                      }
                                      $$ = $2;
                                    }
  |  pattern_opt_stmt error_message_stmt { if (yang_read_message(trg, actual, s, "pattern", ERROR_MESSAGE_KEYWORD)) {
                                             YYABORT;
                                           }
                                           s = NULL;
                                         }
  |  pattern_opt_stmt error_app_tag_stmt { if (yang_read_message(trg, actual, s, "pattern", ERROR_APP_TAG_KEYWORD)) {
                                             YYABORT;
                                           }
                                           s = NULL;
                                         }
  |  pattern_opt_stmt description_stmt { if (yang_read_description(trg, actual, s, "pattern", NODE)) {
                                           YYABORT;
                                          }
                                          s = NULL;
                                        }
  |  pattern_opt_stmt reference_stmt { if (yang_read_reference(trg, actual, s, "pattern", NODE)) {
                                         YYABORT;
                                       }
                                       s = NULL;
                                     }

modifier_arg: string { backup_type = actual_type;
                       actual_type = MODIFIER_KEYWORD;
                     }

modifier_stmt: MODIFIER_KEYWORD sep modifier_arg stmtend { if (!strcmp(s, "invert-match")) {
                                                             $$ = 0x15;
                                                             free(s);
                                                             s = NULL;
                                                           } else {
                                                             LOGVAL(LYE_INSTMT, LY_VLOG_NONE, NULL, s);
                                                             free(s);
                                                             YYABORT;
                                                           }
                                                         }

enum_specification: enum_stmt stmtsep enum_stmts { struct lys_type_enum * tmp;

                                                   cnt_val = 0;
                                                   tmp = realloc(((struct yang_type *)actual)->type->info.enums.enm,
                                                                 ((struct yang_type *)actual)->type->info.enums.count * sizeof *tmp);
                                                   if (!tmp) {
                                                     LOGMEM;
                                                     YYABORT;
                                                   }
                                                   ((struct yang_type *)actual)->type->info.enums.enm = tmp;
                                                 }

enum_stmts: @EMPTYDIR@
  | enum_stmts enum_stmt stmtsep;


enum_stmt: ENUM_KEYWORD sep enum_arg_str enum_end
           { if (yang_check_enum(yang_type, actual, &cnt_val, is_value)) {
               YYABORT;
             }
             actual = $3.actual;
             actual_type = $3.token;
           }

enum_arg_str: string { $$.token = actual_type;
                       $$.actual = yang_type = actual;
                       YANG_ADDELEM(((struct yang_type *)actual)->type->info.enums.enm, ((struct yang_type *)actual)->type->info.enums.count);
                       if (yang_read_enum(trg, yang_type, actual, s)) {
                         YYABORT;
                       }
                       s = NULL;
                       is_value = 0;
                       actual_type = ENUM_KEYWORD;
                     }

enum_end: ';'
  |  '{' stmtsep
         enum_opt_stmt
     '}' { if (((struct lys_type_enum *)actual)->iffeature_size) {
             struct lys_iffeature *tmp;

             tmp = realloc(((struct lys_type_enum *)actual)->iffeature,
                           ((struct lys_type_enum *)actual)->iffeature_size * sizeof *tmp);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             ((struct lys_type_enum *)actual)->iffeature = tmp;
           }
         }

enum_opt_stmt: @EMPTYDIR@
  |  enum_opt_stmt if_feature_stmt stmtsep
  |  enum_opt_stmt value_stmt { if (is_value) {
                                  LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "value", "enum");
                                  YYABORT;
                                }
                                ((struct lys_type_enum *)actual)->value = $2;

                                /* keep the highest enum value for automatic increment */
                                if ($2 >= cnt_val) {
                                  cnt_val = $2 + 1;
                                }
                                is_value = 1;
                              }
  |  enum_opt_stmt status_stmt { if (((struct lys_type_enum *)actual)->flags & LYS_STATUS_MASK) {
                                   LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "status", "enum");
                                   YYABORT;
                                 }
                                 ((struct lys_type_enum *)actual)->flags |= $2;
                               }
  |  enum_opt_stmt description_stmt { if (yang_read_description(trg, actual, s, "enum", NODE)) {
                                        YYABORT;
                                      }
                                      s = NULL;
                                    }
  |  enum_opt_stmt reference_stmt { if (yang_read_reference(trg, actual, s, "enum", NODE)) {
                                      YYABORT;
                                    }
                                    s = NULL;
                                  }

value_arg: integer_value_arg_str { $$ = $1;
                                   backup_type = actual_type;
                                   actual_type = VALUE_KEYWORD;
                                 }

value_stmt: VALUE_KEYWORD sep value_arg stmtend { $$ = $3; }

integer_value_arg_str: integer_value optsep { $$ = $1; }
  |  string_1 { /* convert it to int32_t */
                int64_t val;
                char *endptr;

                val = strtoll(s, &endptr, 10);
                if (val < INT32_MIN || val > INT32_MAX || *endptr) {
                    LOGVAL(LYE_INARG, LY_VLOG_NONE, NULL, s, "value");
                    free(s);
                    YYABORT;
                }
                free(s);
                s = NULL;
                $$ = (int32_t) val;
             }

range_stmt: RANGE_KEYWORD sep range_arg_str range_end { actual_type = $3.token;
                                                        actual = $3.actual;
                                                      }


range_end: ';'
  |  '{' stmtsep
         message_opt_stmt
      '}'
   ;

path_arg: path_arg_str { backup_type = actual_type;
                         actual_type = PATH_KEYWORD;
                       }

path_stmt: PATH_KEYWORD sep path_arg stmtend

require_instance_arg: require_instance_arg_str { $$ = $1;
                                                 backup_type = actual_type;
                                                 actual_type = REQUIRE_INSTANCE_KEYWORD;
                                               }

require_instance_stmt: REQUIRE_INSTANCE_KEYWORD sep require_instance_arg stmtend { $$ = $3; }

require_instance_arg_str: TRUE_KEYWORD optsep { $$ = 1; }
  |  FALSE_KEYWORD optsep { $$ = -1; }
  |  string_1 { if (!strcmp(s,"true")) {
                  $$ = 1;
                } else if (!strcmp(s,"false")) {
                  $$ = -1;
                } else {
                  LOGVAL(LYE_INARG, LY_VLOG_NONE, NULL, s, "require-instance");
                  free(s);
                  YYABORT;
                }
                free(s);
                s = NULL;
              }

bits_specification: bit_stmt bit_stmts { struct lys_type_bit * tmp;

                                         cnt_val = 0;
                                         tmp = realloc(((struct yang_type *)actual)->type->info.bits.bit,
                                                       ((struct yang_type *)actual)->type->info.bits.count * sizeof *tmp);
                                         if (!tmp) {
                                           LOGMEM;
                                           YYABORT;
                                         }
                                         ((struct yang_type *)actual)->type->info.bits.bit = tmp;
                                       }

bit_stmts: @EMPTYDIR@
  | bit_stmts bit_stmt;

bit_stmt: BIT_KEYWORD sep bit_arg_str bit_end
          stmtsep { if (yang_check_bit(yang_type, actual, &cnt_val, is_value)) {
                      YYABORT;
                    }
                    actual = $3.actual;
                    actual_type = $3.token;
                  }

bit_arg_str: identifier_arg_str { $$.token = actual_type;
                                  $$.actual = yang_type = actual;
                                  YANG_ADDELEM(((struct yang_type *)actual)->type->info.bits.bit,
                                               ((struct yang_type *)actual)->type->info.bits.count);
                                  if (yang_read_bit(trg, yang_type, actual, s)) {
                                    YYABORT;
                                  }
                                  s = NULL;
                                  is_value = 0;
                                  actual_type = BIT_KEYWORD;
                                }

bit_end: ';'
  |  '{' stmtsep
         bit_opt_stmt
     '}' { if (((struct lys_type_bit *)actual)->iffeature_size) {
             struct lys_iffeature *tmp;

             tmp = realloc(((struct lys_type_bit *)actual)->iffeature,
                           ((struct lys_type_bit *)actual)->iffeature_size * sizeof *tmp);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             ((struct lys_type_bit *)actual)->iffeature = tmp;
           }
         }

bit_opt_stmt: @EMPTYDIR@
  |  bit_opt_stmt if_feature_stmt stmtsep
  |  bit_opt_stmt position_stmt { if (is_value) {
                                    LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "position", "bit");
                                    YYABORT;
                                  }
                                  ((struct lys_type_bit *)actual)->pos = $2;

                                  /* keep the highest position value for automatic increment */
                                  if ($2 >= cnt_val) {
                                    cnt_val = $2 + 1;
                                  }
                                  is_value = 1;
                                }
  |  bit_opt_stmt status_stmt { if (((struct lys_type_bit *)actual)->flags & LYS_STATUS_MASK) {
                                   LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "status", "bit");
                                   YYABORT;
                                 }
                                 ((struct lys_type_bit *)actual)->flags |= $2;
                              }
  |  bit_opt_stmt description_stmt { if (yang_read_description(trg, actual, s, "bit", NODE)) {
                                       YYABORT;
                                     }
                                     s = NULL;
                                   }
  |  bit_opt_stmt reference_stmt { if (yang_read_reference(trg, actual, s, "bit", NODE)) {
                                     YYABORT;
                                   }
                                   s = NULL;
                                 }

position_value_arg: position_value_arg_str { $$ = $1;
                                             backup_type = actual_type;
                                             actual_type = POSITION_KEYWORD;
                                           }

position_stmt: POSITION_KEYWORD sep position_value_arg stmtend { $$ = $3; }

position_value_arg_str: non_negative_integer_value optsep { $$ = $1; }
  |  string_1 { /* convert it to uint32_t */
                unsigned long val;
                char *endptr = NULL;
                errno = 0;

                val = strtoul(s, &endptr, 10);
                if (s[0] == '-' || *endptr || errno || val > UINT32_MAX) {
                  LOGVAL(LYE_INARG, LY_VLOG_NONE, NULL, s, "position");
                  free(s);
                  YYABORT;
                }
                free(s);
                s = NULL;
                $$ = (uint32_t) val;
              }

error_message_arg: string { backup_type = actual_type;
                            actual_type = ERROR_MESSAGE_KEYWORD;
                          }

error_message_stmt: ERROR_MESSAGE_KEYWORD sep error_message_arg stmtend

error_app_tag_arg: string { backup_type = actual_type;
                            actual_type = ERROR_APP_TAG_KEYWORD;
                          }

error_app_tag_stmt: ERROR_APP_TAG_KEYWORD sep error_app_tag_arg stmtend

units_arg: string { backup_type = actual_type;
                    actual_type = UNITS_KEYWORD;
                  }

units_stmt: UNITS_KEYWORD sep units_arg stmtend

default_arg: string { backup_type = actual_type;
                      actual_type = DEFAULT_KEYWORD;
                    }

default_stmt: DEFAULT_KEYWORD sep default_arg stmtend

grouping_arg_str: identifier_arg_str { $$.token = actual_type;
                                       $$.actual = actual;
                                       if (!(actual = yang_read_node(trg, actual, param->node, s, LYS_GROUPING, sizeof(struct lys_node_grp)))) {
                                         YYABORT;
                                       }
                                       s = NULL;
                                       data_node = actual;
                                       actual_type = GROUPING_KEYWORD;
                                     }

grouping_stmt: GROUPING_KEYWORD sep grouping_arg_str grouping_end
               { LOGDBG(LY_LDGYANG, "finished parsing grouping statement \"%s\"", data_node->name);
                 actual_type = $3.token;
                 actual = $3.actual;
                 data_node = $3.actual;
               }

grouping_end: ';'
  |  '{' stmtsep
         grouping_opt_stmt
     '}'

grouping_opt_stmt: @EMPTYDIR@ { $$.grouping = actual; }
  |  grouping_opt_stmt status_stmt { if ($1.grouping->flags & LYS_STATUS_MASK) {
                                       LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.grouping, "status", "grouping");
                                       YYABORT;
                                     }
                                     $1.grouping->flags |= $2;
                                   }
  |  grouping_opt_stmt description_stmt { if (yang_read_description(trg, $1.grouping, s, "grouping", NODE_PRINT)) {
                                            YYABORT;
                                          }
                                          s = NULL;
                                        }
  |  grouping_opt_stmt reference_stmt { if (yang_read_reference(trg, $1.grouping, s, "grouping", NODE_PRINT)) {
                                          YYABORT;
                                        }
                                        s = NULL;
                                      }
  |  grouping_opt_stmt grouping_stmt stmtsep
  |  grouping_opt_stmt typedef_stmt stmtsep
  |  grouping_opt_stmt data_def_stmt stmtsep
  |  grouping_opt_stmt action_stmt stmtsep
  |  grouping_opt_stmt notification_stmt stmtsep { if (trg->version < 2) {
                                                     LOGVAL(LYE_INSTMT, LY_VLOG_LYS, $1.grouping, "notification");
                                                     YYABORT;
                                                   }
                                                 }

data_def_stmt: container_stmt
  |  leaf_stmt
  |  leaf_list_stmt
  |  list_stmt
  |  choice_stmt
  |  anyxml_stmt
  |  anydata_stmt
  |  uses_stmt
  ;

container_arg_str: identifier_arg_str { $$.token = actual_type;
                                        $$.actual = actual;
                                        if (!(actual = yang_read_node(trg, actual, param->node, s, LYS_CONTAINER, sizeof(struct lys_node_container)))) {
                                          YYABORT;
                                        }
                                        data_node = actual;
                                        s = NULL;
                                        actual_type = CONTAINER_KEYWORD;
                                      }

container_stmt: CONTAINER_KEYWORD sep container_arg_str container_end
                { LOGDBG(LY_LDGYANG, "finished parsing container statement \"%s\"", data_node->name);
                  actual_type = $3.token;
                  actual = $3.actual;
                  data_node = $3.actual;
                }

container_end: ';'
  |  '{' stmtsep
         container_opt_stmt
      '}' { void *tmp;

            if ($3.container->iffeature_size) {
              tmp = realloc($3.container->iffeature, $3.container->iffeature_size * sizeof *$3.container->iffeature);
              if (!tmp) {
                LOGMEM;
                YYABORT;
              }
              $3.container->iffeature = tmp;
            }

            if ($3.container->must_size) {
              tmp = realloc($3.container->must, $3.container->must_size * sizeof *$3.container->must);
              if (!tmp) {
                LOGMEM;
                YYABORT;
              }
              $3.container->must = tmp;
            }
          }

container_opt_stmt: @EMPTYDIR@ { $$.container = actual; }
  |  container_opt_stmt when_stmt stmtsep
  |  container_opt_stmt if_feature_stmt stmtsep
  |  container_opt_stmt must_stmt stmtsep
  |  container_opt_stmt presence_stmt { if (yang_read_presence(trg, $1.container, s)) {
                                          YYABORT;
                                        }
                                        s = NULL;
                                      }
  |  container_opt_stmt config_stmt { if ($1.container->flags & LYS_CONFIG_MASK) {
                                        LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.container, "config", "container");
                                        YYABORT;
                                      }
                                      $1.container->flags |= $2;
                                    }
  |  container_opt_stmt status_stmt { if ($1.container->flags & LYS_STATUS_MASK) {
                                        LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.container, "status", "container");
                                        YYABORT;
                                      }
                                      $1.container->flags |= $2;
                                    }
  |  container_opt_stmt description_stmt { if (yang_read_description(trg, $1.container, s, "container", NODE_PRINT)) {
                                             YYABORT;
                                           }
                                           s = NULL;
                                         }
  |  container_opt_stmt reference_stmt { if (yang_read_reference(trg, $1.container, s, "container", NODE_PRINT)) {
                                           YYABORT;
                                         }
                                         s = NULL;
                                       }
  |  container_opt_stmt grouping_stmt stmtsep
  |  container_opt_stmt action_stmt stmtsep
  |  container_opt_stmt notification_stmt stmtsep { if (trg->version < 2) {
                                                      LOGVAL(LYE_INSTMT, LY_VLOG_LYS, $1.container, "notification");
                                                      YYABORT;
                                                    }
                                                  }
  |  container_opt_stmt typedef_stmt stmtsep
  |  container_opt_stmt data_def_stmt stmtsep

leaf_stmt: LEAF_KEYWORD sep leaf_arg_str
           '{' stmtsep
               leaf_opt_stmt
            '}' { void *tmp;

                  if (!($6.node.flag & LYS_TYPE_DEF)) {
                    LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_LYS, $6.node.ptr_leaf, "type", "leaf");
                    YYABORT;
                  }
                  if ($6.node.ptr_leaf->dflt && ($6.node.ptr_leaf->flags & LYS_MAND_TRUE)) {
                    /* RFC 6020, 7.6.4 - default statement must not with mandatory true */
                    LOGVAL(LYE_INCHILDSTMT, LY_VLOG_LYS, $6.node.ptr_leaf, "mandatory", "leaf");
                    LOGVAL(LYE_SPEC, LY_VLOG_LYS, $6.node.ptr_leaf, "The \"mandatory\" statement is forbidden on leaf with \"default\".");
                    YYABORT;
                  }

                  if ($6.node.ptr_leaf->iffeature_size) {
                    tmp = realloc($6.node.ptr_leaf->iffeature, $6.node.ptr_leaf->iffeature_size * sizeof *$6.node.ptr_leaf->iffeature);
                    if (!tmp) {
                      LOGMEM;
                      YYABORT;
                    }
                    $6.node.ptr_leaf->iffeature = tmp;
                  }

                  if ($6.node.ptr_leaf->must_size) {
                    tmp = realloc($6.node.ptr_leaf->must, $6.node.ptr_leaf->must_size * sizeof *$6.node.ptr_leaf->must);
                    if (!tmp) {
                      LOGMEM;
                      YYABORT;
                    }
                    $6.node.ptr_leaf->must = tmp;
                  }

                  LOGDBG(LY_LDGYANG, "finished parsing leaf statement \"%s\"", data_node->name);
                  actual_type = $3.token;
                  actual = $3.actual;
                  data_node = $3.actual;
                }

leaf_arg_str: identifier_arg_str { $$.token = actual_type;
                                   $$.actual = actual;
                                   if (!(actual = yang_read_node(trg, actual, param->node, s, LYS_LEAF, sizeof(struct lys_node_leaf)))) {
                                     YYABORT;
                                   }
                                   data_node = actual;
                                   s = NULL;
                                   actual_type = LEAF_KEYWORD;
                                 }

leaf_opt_stmt: @EMPTYDIR@ { $$.node.ptr_leaf = actual;
                            $$.node.flag = 0;
                          }
  |  leaf_opt_stmt when_stmt stmtsep
  |  leaf_opt_stmt if_feature_stmt stmtsep
  |  leaf_opt_stmt type_stmt stmtsep { $1.node.flag |= LYS_TYPE_DEF;
                                       $$ = $1;
                                     }
  |  leaf_opt_stmt units_stmt { if (yang_read_units(trg, $1.node.ptr_leaf, s, LEAF_KEYWORD)) {
                                  YYABORT;
                                }
                                s = NULL;
                              }
  |  leaf_opt_stmt must_stmt stmtsep
  |  leaf_opt_stmt default_stmt { if (yang_read_default(trg, $1.node.ptr_leaf, s, LEAF_KEYWORD)) {
                                    YYABORT;
                                  }
                                  s = NULL;
                                }
  |  leaf_opt_stmt config_stmt { if ($1.node.ptr_leaf->flags & LYS_CONFIG_MASK) {
                                   LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_leaf, "config", "leaf");
                                   YYABORT;
                                 }
                                 $1.node.ptr_leaf->flags |= $2;
                               }
  |  leaf_opt_stmt mandatory_stmt { if ($1.node.ptr_leaf->flags & LYS_MAND_MASK) {
                                      LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_leaf, "mandatory", "leaf");
                                      YYABORT;
                                    }
                                    $1.node.ptr_leaf->flags |= $2;
                                  }
  |  leaf_opt_stmt status_stmt { if ($1.node.ptr_leaf->flags & LYS_STATUS_MASK) {
                                   LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_leaf, "status", "leaf");
                                   YYABORT;
                                 }
                                 $1.node.ptr_leaf->flags |= $2;
                               }
  |  leaf_opt_stmt description_stmt { if (yang_read_description(trg, $1.node.ptr_leaf, s, "leaf", NODE_PRINT)) {
                                        YYABORT;
                                      }
                                      s = NULL;
                                    }
  |  leaf_opt_stmt reference_stmt { if (yang_read_reference(trg, $1.node.ptr_leaf, s, "leaf", NODE_PRINT)) {
                                      YYABORT;
                                    }
                                    s = NULL;
                                  }

leaf_list_arg_str: identifier_arg_str { $$.token = actual_type;
                                        $$.actual = actual;
                                        if (!(actual = yang_read_node(trg, actual, param->node, s, LYS_LEAFLIST, sizeof(struct lys_node_leaflist)))) {
                                          YYABORT;
                                        }
                                        data_node = actual;
                                        s = NULL;
                                        actual_type = LEAF_LIST_KEYWORD;
                                      }

  leaf_list_stmt: LEAF_LIST_KEYWORD sep leaf_list_arg_str
                  '{' stmtsep
                      leaf_list_opt_stmt
                  '}' { void *tmp;

                        if ($6.node.ptr_leaflist->flags & LYS_CONFIG_R) {
                          /* RFC 6020, 7.7.5 - ignore ordering when the list represents state data
                           * ignore oredering MASK - 0x7F
                           */
                          $6.node.ptr_leaflist->flags &= 0x7F;
                        }
                        if (!($6.node.flag & LYS_TYPE_DEF)) {
                          LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_LYS, $6.node.ptr_leaflist, "type", "leaf-list");
                          YYABORT;
                        }
                        if ($6.node.ptr_leaflist->dflt_size && $6.node.ptr_leaflist->min) {
                          LOGVAL(LYE_INCHILDSTMT, LY_VLOG_LYS, $6.node.ptr_leaflist, "min-elements", "leaf-list");
                          LOGVAL(LYE_SPEC, LY_VLOG_LYS, $6.node.ptr_leaflist,
                                 "The \"min-elements\" statement with non-zero value is forbidden on leaf-lists with the \"default\" statement.");
                          YYABORT;
                        }

                        if ($6.node.ptr_leaflist->iffeature_size) {
                          tmp = realloc($6.node.ptr_leaflist->iffeature, $6.node.ptr_leaflist->iffeature_size * sizeof *$6.node.ptr_leaflist->iffeature);
                          if (!tmp) {
                            LOGMEM;
                            YYABORT;
                          }
                          $6.node.ptr_leaflist->iffeature = tmp;
                        }

                        if ($6.node.ptr_leaflist->must_size) {
                          tmp = realloc($6.node.ptr_leaflist->must, $6.node.ptr_leaflist->must_size * sizeof *$6.node.ptr_leaflist->must);
                          if (!tmp) {
                            LOGMEM;
                            YYABORT;
                          }
                          $6.node.ptr_leaflist->must = tmp;
                        }

                        if ($6.node.ptr_leaflist->dflt_size) {
                          tmp = realloc($6.node.ptr_leaflist->dflt, $6.node.ptr_leaflist->dflt_size * sizeof *$6.node.ptr_leaflist->dflt);
                          if (!tmp) {
                            LOGMEM;
                            YYABORT;
                          }
                          $6.node.ptr_leaflist->dflt = tmp;
                        }

                        LOGDBG(LY_LDGYANG, "finished parsing leaf-list statement \"%s\"", data_node->name);
                        actual_type = $3.token;
                        actual = $3.actual;
                        data_node = $3.actual;
                      }

leaf_list_opt_stmt: @EMPTYDIR@ { $$.node.ptr_leaflist = actual;
                                 $$.node.flag = 0;
                               }
  |  leaf_list_opt_stmt when_stmt stmtsep
  |  leaf_list_opt_stmt if_feature_stmt stmtsep
  |  leaf_list_opt_stmt type_stmt stmtsep { $1.node.flag |= LYS_TYPE_DEF;
                                            $$ = $1;
                                          }
  |  leaf_list_opt_stmt default_stmt { if (trg->version < 2) {
                                         free(s);
                                         LOGVAL(LYE_INSTMT, LY_VLOG_LYS, $1.node.ptr_leaflist, "default");
                                         YYABORT;
                                       }
                                       YANG_ADDELEM($1.node.ptr_leaflist->dflt,
                                                    $1.node.ptr_leaflist->dflt_size);
                                       (*(const char **)actual) = lydict_insert_zc(param->module->ctx, s);
                                       s = NULL;
                                       actual = $1.node.ptr_leaflist;
                                     }
  |  leaf_list_opt_stmt units_stmt { if (yang_read_units(trg, $1.node.ptr_leaflist, s, LEAF_LIST_KEYWORD)) {
                                       YYABORT;
                                     }
                                     s = NULL;
                                   }
  |  leaf_list_opt_stmt must_stmt stmtsep
  |  leaf_list_opt_stmt config_stmt { if ($1.node.ptr_leaflist->flags & LYS_CONFIG_MASK) {
                                        LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_leaflist, "config", "leaf-list");
                                        YYABORT;
                                      }
                                      $1.node.ptr_leaflist->flags |= $2;
                                    }
  |  leaf_list_opt_stmt min_elements_stmt { if ($1.node.flag & LYS_MIN_ELEMENTS) {
                                              LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_leaflist, "min-elements", "leaf-list");
                                              YYABORT;
                                            }
                                            $1.node.ptr_leaflist->min = $2;
                                            $1.node.flag |= LYS_MIN_ELEMENTS;
                                            $$ = $1;
                                            if ($1.node.ptr_leaflist->max && ($1.node.ptr_leaflist->min > $1.node.ptr_leaflist->max)) {
                                              LOGVAL(LYE_SPEC, LY_VLOG_LYS, $1.node.ptr_leaflist, "Invalid value \"%d\" of \"%s\".", $2, "min-elements");
                                              LOGVAL(LYE_SPEC, LY_VLOG_LYS, $1.node.ptr_leaflist, "\"min-elements\" is bigger than \"max-elements\".");
                                              YYABORT;
                                            }
                                          }
  |  leaf_list_opt_stmt max_elements_stmt { if ($1.node.flag & LYS_MAX_ELEMENTS) {
                                              LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_leaflist, "max-elements", "leaf-list");
                                              YYABORT;
                                            }
                                            $1.node.ptr_leaflist->max = $2;
                                            $1.node.flag |= LYS_MAX_ELEMENTS;
                                            $$ = $1;
                                            if ($1.node.ptr_leaflist->min > $1.node.ptr_leaflist->max) {
                                              LOGVAL(LYE_SPEC, LY_VLOG_LYS, $1.node.ptr_leaflist, "Invalid value \"%d\" of \"%s\".", $2, "max-elements");
                                              LOGVAL(LYE_SPEC, LY_VLOG_LYS, $1.node.ptr_leaflist, "\"max-elements\" is smaller than \"min-elements\".");
                                              YYABORT;
                                            }
                                          }
  |  leaf_list_opt_stmt ordered_by_stmt { if ($1.node.flag & LYS_ORDERED_MASK) {
                                            LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_leaflist, "ordered by", "leaf-list");
                                            YYABORT;
                                          }
                                          if ($2 & LYS_USERORDERED) {
                                            $1.node.ptr_leaflist->flags |= LYS_USERORDERED;
                                          }
                                          $1.node.flag |= $2;
                                          $$ = $1;
                                        }
  |  leaf_list_opt_stmt status_stmt { if ($1.node.ptr_leaflist->flags & LYS_STATUS_MASK) {
                                        LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_leaflist, "status", "leaf-list");
                                        YYABORT;
                                      }
                                      $1.node.ptr_leaflist->flags |= $2;
                                    }
  |  leaf_list_opt_stmt description_stmt { if (yang_read_description(trg, $1.node.ptr_leaflist, s, "leaf-list", NODE_PRINT)) {
                                             YYABORT;
                                           }
                                           s = NULL;
                                         }
  |  leaf_list_opt_stmt reference_stmt { if (yang_read_reference(trg, $1.node.ptr_leaflist, s, "leaf-list", NODE_PRINT)) {
                                           YYABORT;
                                         }
                                         s = NULL;
                                       }

list_arg_str: identifier_arg_str { $$.token = actual_type;
                                   $$.actual = actual;
                                   if (!(actual = yang_read_node(trg, actual, param->node, s, LYS_LIST, sizeof(struct lys_node_list)))) {
                                     YYABORT;
                                   }
                                   data_node = actual;
                                   s = NULL;
                                   actual_type = LIST_KEYWORD;
                                 }

list_stmt: LIST_KEYWORD sep list_arg_str
           '{' stmtsep
               list_opt_stmt
            '}' { void *tmp;

                  if ($6.node.ptr_list->iffeature_size) {
                    tmp = realloc($6.node.ptr_list->iffeature, $6.node.ptr_list->iffeature_size * sizeof *$6.node.ptr_list->iffeature);
                    if (!tmp) {
                      LOGMEM;
                      YYABORT;
                    }
                    $6.node.ptr_list->iffeature = tmp;
                  }

                  if ($6.node.ptr_list->must_size) {
                    tmp = realloc($6.node.ptr_list->must, $6.node.ptr_list->must_size * sizeof *$6.node.ptr_list->must);
                    if (!tmp) {
                      LOGMEM;
                      YYABORT;
                    }
                    $6.node.ptr_list->must = tmp;
                  }

                  if ($6.node.ptr_list->tpdf_size) {
                    tmp = realloc($6.node.ptr_list->tpdf, $6.node.ptr_list->tpdf_size * sizeof *$6.node.ptr_list->tpdf);
                    if (!tmp) {
                      LOGMEM;
                      YYABORT;
                    }
                    $6.node.ptr_list->tpdf = tmp;
                  }

                  if ($6.node.ptr_list->unique_size) {
                    tmp = realloc($6.node.ptr_list->unique, $6.node.ptr_list->unique_size * sizeof *$6.node.ptr_list->unique);
                    if (!tmp) {
                      LOGMEM;
                      YYABORT;
                    }
                    $6.node.ptr_list->unique = tmp;
                  }

                  LOGDBG(LY_LDGYANG, "finished parsing list statement \"%s\"", data_node->name);
                  actual_type = $3.token;
                  actual = $3.actual;
                  data_node = $3.actual;
                }

list_opt_stmt: @EMPTYDIR@ { $$.node.ptr_list = actual;
                            $$.node.flag = 0;
                          }
  |  list_opt_stmt when_stmt stmtsep
  |  list_opt_stmt if_feature_stmt stmtsep
  |  list_opt_stmt must_stmt stmtsep
  |  list_opt_stmt key_stmt { if ($1.node.ptr_list->keys) {
                                  LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_list, "key", "list");
                                  free(s);
                                  YYABORT;
                              }
                              $1.node.ptr_list->keys = (struct lys_node_leaf **)s;
                              $$ = $1;
                              s = NULL;
                            }
  |  list_opt_stmt unique_stmt { YANG_ADDELEM($1.node.ptr_list->unique, $1.node.ptr_list->unique_size);
                                 ((struct lys_unique *)actual)->expr = (const char **)s;
                                 $$ = $1;
                                 s = NULL;
                                 actual = $1.node.ptr_list;
                               }
  |  list_opt_stmt config_stmt { if ($1.node.ptr_list->flags & LYS_CONFIG_MASK) {
                                   LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_list, "config", "list");
                                   YYABORT;
                                 }
                                 $1.node.ptr_list->flags |= $2;
                               }
  |  list_opt_stmt min_elements_stmt { if ($1.node.flag & LYS_MIN_ELEMENTS) {
                                         LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_list, "min-elements", "list");
                                         YYABORT;
                                       }
                                       $1.node.ptr_list->min = $2;
                                       $1.node.flag |= LYS_MIN_ELEMENTS;
                                       $$ = $1;
                                       if ($1.node.ptr_list->max && ($1.node.ptr_list->min > $1.node.ptr_list->max)) {
                                         LOGVAL(LYE_SPEC, LY_VLOG_LYS, $1.node.ptr_list, "Invalid value \"%d\" of \"%s\".", $2, "min-elements");
                                         LOGVAL(LYE_SPEC, LY_VLOG_LYS, $1.node.ptr_list, "\"min-elements\" is bigger than \"max-elements\".");
                                         YYABORT;
                                       }
                                     }
  |  list_opt_stmt max_elements_stmt { if ($1.node.flag & LYS_MAX_ELEMENTS) {
                                         LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_list, "max-elements", "list");
                                         YYABORT;
                                       }
                                       $1.node.ptr_list->max = $2;
                                       $1.node.flag |= LYS_MAX_ELEMENTS;
                                       $$ = $1;
                                       if ($1.node.ptr_list->min > $1.node.ptr_list->max) {
                                         LOGVAL(LYE_SPEC, LY_VLOG_LYS, $1.node.ptr_list, "Invalid value \"%d\" of \"%s\".", $2, "min-elements");
                                         LOGVAL(LYE_SPEC, LY_VLOG_LYS, $1.node.ptr_list, "\"max-elements\" is smaller than \"min-elements\".");
                                         YYABORT;
                                       }
                                     }
  |  list_opt_stmt ordered_by_stmt { if ($1.node.flag & LYS_ORDERED_MASK) {
                                       LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_list, "ordered by", "list");
                                       YYABORT;
                                     }
                                     if ($2 & LYS_USERORDERED) {
                                       $1.node.ptr_list->flags |= LYS_USERORDERED;
                                     }
                                     $1.node.flag |= $2;
                                     $$ = $1;
                                   }
  |  list_opt_stmt status_stmt { if ($1.node.ptr_list->flags & LYS_STATUS_MASK) {
                                   LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_list, "status", "list");
                                   YYABORT;
                                 }
                                 $1.node.ptr_list->flags |= $2;
                               }
  |  list_opt_stmt description_stmt { if (yang_read_description(trg, $1.node.ptr_list, s, "list", NODE_PRINT)) {
                                        YYABORT;
                                      }
                                      s = NULL;
                                    }
  |  list_opt_stmt reference_stmt { if (yang_read_reference(trg, $1.node.ptr_list, s, "list", NODE_PRINT)) {
                                      YYABORT;
                                    }
                                    s = NULL;
                                  }
  |  list_opt_stmt typedef_stmt stmtsep
  |  list_opt_stmt grouping_stmt stmtsep
  |  list_opt_stmt action_stmt stmtsep
  |  list_opt_stmt notification_stmt stmtsep { if (trg->version < 2) {
                                                 LOGVAL(LYE_INSTMT, LY_VLOG_LYS, $1.node.ptr_list, "notification");
                                                 YYABORT;
                                               }
                                             }
  |  list_opt_stmt data_def_stmt stmtsep

choice_arg_str: identifier_arg_str { $$.token = actual_type;
                                     $$.actual = actual;
                                     if (!(actual = yang_read_node(trg, actual, param->node, s, LYS_CHOICE, sizeof(struct lys_node_choice)))) {
                                       YYABORT;
                                     }
                                     data_node = actual;
                                     s = NULL;
                                     actual_type = CHOICE_KEYWORD;
                                   }

choice_stmt: CHOICE_KEYWORD sep choice_arg_str choice_end
             { LOGDBG(LY_LDGYANG, "finished parsing choice statement \"%s\"", data_node->name);
               actual_type = $3.token;
               actual = $3.actual;
               data_node = $3.actual;
             }

choice_end: ';'
  |  '{' stmtsep
         choice_opt_stmt
     '}' { struct lys_iffeature *tmp;

           if (($3.node.ptr_choice->flags & LYS_MAND_TRUE) && $3.node.ptr_choice->dflt) {
              LOGVAL(LYE_INCHILDSTMT, LY_VLOG_LYS, $3.node.ptr_choice, "default", "choice");
              LOGVAL(LYE_SPEC, LY_VLOG_LYS, $3.node.ptr_choice, "The \"default\" statement is forbidden on choices with \"mandatory\".");
              YYABORT;
            }

           if ($3.node.ptr_choice->iffeature_size) {
             tmp = realloc($3.node.ptr_choice->iffeature, $3.node.ptr_choice->iffeature_size * sizeof *tmp);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             $3.node.ptr_choice->iffeature = tmp;
           }
         }

choice_opt_stmt: @EMPTYDIR@ { $$.node.ptr_choice = actual;
                              $$.node.flag = 0;
                            }
  |  choice_opt_stmt when_stmt stmtsep
  |  choice_opt_stmt if_feature_stmt stmtsep
  |  choice_opt_stmt default_stmt { if ($1.node.flag & LYS_CHOICE_DEFAULT) {
                                      LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_choice, "default", "choice");
                                      free(s);
                                      YYABORT;
                                    }
                                    $1.node.ptr_choice->dflt = (struct lys_node *) s;
                                    s = NULL;
                                    $$ = $1;
                                    $$.node.flag |= LYS_CHOICE_DEFAULT;
                                  }
  |  choice_opt_stmt config_stmt { if ($1.node.ptr_choice->flags & LYS_CONFIG_MASK) {
                                     LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_choice, "config", "choice");
                                     YYABORT;
                                   }
                                   $1.node.ptr_choice->flags |= $2;
                                   $$ = $1;
                                 }
|  choice_opt_stmt mandatory_stmt { if ($1.node.ptr_choice->flags & LYS_MAND_MASK) {
                                      LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_choice, "mandatory", "choice");
                                      YYABORT;
                                    }
                                    $1.node.ptr_choice->flags |= $2;
                                    $$ = $1;
                                  }
  |  choice_opt_stmt status_stmt { if ($1.node.ptr_choice->flags & LYS_STATUS_MASK) {
                                     LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_choice, "status", "choice");
                                     YYABORT;
                                   }
                                   $1.node.ptr_choice->flags |= $2;
                                   $$ = $1;
                                 }
  |  choice_opt_stmt description_stmt { if (yang_read_description(trg, $1.node.ptr_choice, s, "choice", NODE_PRINT)) {
                                          YYABORT;
                                        }
                                        s = NULL;
                                        $$ = $1;
                                      }
  |  choice_opt_stmt reference_stmt { if (yang_read_reference(trg, $1.node.ptr_choice, s, "choice", NODE_PRINT)) {
                                        YYABORT;
                                      }
                                      s = NULL;
                                      $$ = $1;
                                    }
  |  choice_opt_stmt short_case_case_stmt stmtsep

short_case_case_stmt:  short_case_stmt
  |  case_stmt

short_case_stmt: container_stmt
  |  leaf_stmt
  |  leaf_list_stmt
  |  list_stmt
  |  anyxml_stmt
  |  anydata_stmt
  |  choice_stmt { if (trg->version < 2 ) {
                     LOGVAL(LYE_INSTMT, LY_VLOG_LYS, actual, "choice");
                     YYABORT;
                   }
                 }

case_arg_str: identifier_arg_str { $$.token = actual_type;
                                   $$.actual = actual;
                                   if (!(actual = yang_read_node(trg, actual, param->node, s, LYS_CASE, sizeof(struct lys_node_case)))) {
                                     YYABORT;
                                   }
                                   data_node = actual;
                                   s = NULL;
                                   actual_type = CASE_KEYWORD;
                                 }

case_stmt: CASE_KEYWORD sep case_arg_str case_end
           { LOGDBG(LY_LDGYANG, "finished parsing case statement \"%s\"", data_node->name);
             actual_type = $3.token;
             actual = $3.actual;
             data_node = $3.actual;
           }

case_end: ';'
  |  '{' stmtsep
         case_opt_stmt
      '}' { struct lys_iffeature *tmp;

           if ($3.cs->iffeature_size) {
             tmp = realloc($3.cs->iffeature, $3.cs->iffeature_size * sizeof *tmp);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             $3.cs->iffeature = tmp;
           }
          }

case_opt_stmt: @EMPTYDIR@ { $$.cs = actual; }
  |  case_opt_stmt when_stmt stmtsep
  |  case_opt_stmt if_feature_stmt stmtsep
  |  case_opt_stmt status_stmt { if ($1.cs->flags & LYS_STATUS_MASK) {
                                   LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.cs, "status", "case");
                                   YYABORT;
                                 }
                                 $1.cs->flags |= $2;
                               }
  |  case_opt_stmt description_stmt { if (yang_read_description(trg, $1.cs, s, "case", NODE_PRINT)) {
                                        YYABORT;
                                      }
                                      s = NULL;
                                    }
  |  case_opt_stmt reference_stmt { if (yang_read_reference(trg, $1.cs, s, "case", NODE_PRINT)) {
                                      YYABORT;
                                    }
                                    s = NULL;
                                  }
  |  case_opt_stmt data_def_stmt stmtsep


anyxml_arg_str: identifier_arg_str { $$.token = actual_type;
                                     $$.actual = actual;
                                     if (!(actual = yang_read_node(trg, actual, param->node, s, LYS_ANYXML, sizeof(struct lys_node_anydata)))) {
                                       YYABORT;
                                     }
                                     data_node = actual;
                                     s = NULL;
                                     actual_type = ANYXML_KEYWORD;
                                   }

anyxml_stmt: ANYXML_KEYWORD sep anyxml_arg_str anyxml_end
             { LOGDBG(LY_LDGYANG, "finished parsing anyxml statement \"%s\"", data_node->name);
               actual_type = $3.token;
               actual = $3.actual;
               data_node = $3.actual;
             }

anydata_arg_str: identifier_arg_str { $$.token = actual_type;
                                      $$.actual = actual;
                                      if (!(actual = yang_read_node(trg, actual, param->node, s, LYS_ANYDATA, sizeof(struct lys_node_anydata)))) {
                                        YYABORT;
                                      }
                                      data_node = actual;
                                      s = NULL;
                                      actual_type = ANYDATA_KEYWORD;
                                    }

anydata_stmt: ANYDATA_KEYWORD sep anydata_arg_str anyxml_end
              { LOGDBG(LY_LDGYANG, "finished parsing anydata statement \"%s\"", data_node->name);
                actual_type = $3.token;
                actual = $3.actual;
                data_node = $3.actual;
              }

anyxml_end: ';'
  |  '{' stmtsep
         anyxml_opt_stmt
     '}' { void *tmp;

           if ($3.node.ptr_anydata->iffeature_size) {
             tmp = realloc($3.node.ptr_anydata->iffeature, $3.node.ptr_anydata->iffeature_size * sizeof *$3.node.ptr_anydata->iffeature);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             $3.node.ptr_anydata->iffeature = tmp;
           }

           if ($3.node.ptr_anydata->must_size) {
             tmp = realloc($3.node.ptr_anydata->must, $3.node.ptr_anydata->must_size * sizeof *$3.node.ptr_anydata->must);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             $3.node.ptr_anydata->must = tmp;
           }
         }

anyxml_opt_stmt: @EMPTYDIR@ { $$.node.ptr_anydata = actual;
                              $$.node.flag = actual_type;
                            }
  |  anyxml_opt_stmt when_stmt stmtsep
  |  anyxml_opt_stmt if_feature_stmt stmtsep
  |  anyxml_opt_stmt must_stmt stmtsep
  |  anyxml_opt_stmt config_stmt { if ($1.node.ptr_anydata->flags & LYS_CONFIG_MASK) {
                                     LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_anydata, "config",
                                            ($1.node.flag == ANYXML_KEYWORD) ? "anyxml" : "anydata");
                                     YYABORT;
                                   }
                                   $1.node.ptr_anydata->flags |= $2;
                                 }
  |  anyxml_opt_stmt mandatory_stmt { if ($1.node.ptr_anydata->flags & LYS_MAND_MASK) {
                                        LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_anydata, "mandatory",
                                               ($1.node.flag == ANYXML_KEYWORD) ? "anyxml" : "anydata");
                                        YYABORT;
                                      }
                                      $1.node.ptr_anydata->flags |= $2;
                                    }
  |  anyxml_opt_stmt status_stmt { if ($1.node.ptr_anydata->flags & LYS_STATUS_MASK) {
                                     LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_anydata, "status",
                                            ($1.node.flag == ANYXML_KEYWORD) ? "anyxml" : "anydata");
                                     YYABORT;
                                   }
                                   $1.node.ptr_anydata->flags |= $2;
                                 }
  |  anyxml_opt_stmt description_stmt { if (yang_read_description(trg, $1.node.ptr_anydata, s, ($1.node.flag == ANYXML_KEYWORD) ? "anyxml" : "anydata", NODE_PRINT)) {
                                          YYABORT;
                                        }
                                        s = NULL;
                                      }
  |  anyxml_opt_stmt reference_stmt { if (yang_read_reference(trg, $1.node.ptr_anydata, s, ($1.node.flag == ANYXML_KEYWORD) ? "anyxml" : "anydata", NODE_PRINT)) {
                                        YYABORT;
                                      }
                                      s = NULL;
                                    }

uses_arg_str: identifier_ref_arg_str { $$.token = actual_type;
                                       $$.actual = actual;
                                       if (!(actual = yang_read_node(trg, actual, param->node, s, LYS_USES, sizeof(struct lys_node_uses)))) {
                                         YYABORT;
                                       }
                                       data_node = actual;
                                       s = NULL;
                                       actual_type = USES_KEYWORD;
                                     }

uses_stmt: USES_KEYWORD sep uses_arg_str uses_end
           { LOGDBG(LY_LDGYANG, "finished parsing uses statement \"%s\"", data_node->name);
             actual_type = $3.token;
             actual = $3.actual;
             data_node = $3.actual;
           }

uses_end: ';'
  |  '{' stmtsep
         uses_opt_stmt
     '}' { void *tmp;

           if ($3.uses->iffeature_size) {
             tmp = realloc($3.uses->iffeature, $3.uses->iffeature_size * sizeof *$3.uses->iffeature);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             $3.uses->iffeature = tmp;
           }

           if ($3.uses->refine_size) {
             tmp = realloc($3.uses->refine, $3.uses->refine_size * sizeof *$3.uses->refine);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             $3.uses->refine = tmp;
           }

           if ($3.uses->augment_size) {
             tmp = realloc($3.uses->augment, $3.uses->augment_size * sizeof *$3.uses->augment);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             $3.uses->augment = tmp;
           }
         }

uses_opt_stmt: @EMPTYDIR@ { $$.uses = actual; }
  |  uses_opt_stmt when_stmt stmtsep
  |  uses_opt_stmt if_feature_stmt stmtsep
  |  uses_opt_stmt status_stmt { if ($1.uses->flags & LYS_STATUS_MASK) {
                                   LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.uses, "status", "uses");
                                   YYABORT;
                                 }
                                 $1.uses->flags |= $2;
                               }
  |  uses_opt_stmt description_stmt { if (yang_read_description(trg, $1.uses, s, "uses", NODE_PRINT)) {
                                        YYABORT;
                                      }
                                      s = NULL;
                                    }
  |  uses_opt_stmt reference_stmt { if (yang_read_reference(trg, $1.uses, s, "uses", NODE_PRINT)) {
                                      YYABORT;
                                    }
                                    s = NULL;
                                  }
  |  uses_opt_stmt refine_stmt stmtsep
  |  uses_opt_stmt uses_augment_stmt stmtsep

refine_args_str: descendant_schema_nodeid optsep
  | string_1

refine_arg_str: refine_args_str { $$.token = actual_type;
                                  $$.actual = actual;
                                  YANG_ADDELEM(((struct lys_node_uses *)actual)->refine,
                                               ((struct lys_node_uses *)actual)->refine_size);
                                  ((struct lys_refine *)actual)->target_name = transform_schema2json(trg, s);
                                  free(s);
                                  s = NULL;
                                  if (!((struct lys_refine *)actual)->target_name) {
                                    YYABORT;
                                  }
                                  actual_type = REFINE_KEYWORD;
                                }

refine_stmt: REFINE_KEYWORD sep refine_arg_str refine_end
             { actual_type = $3.token;
               actual = $3.actual;
             }

refine_end: ';'
  |  '{' stmtsep
         refine_body_opt_stmts
     '}' { void *tmp;

           if ($3.refine->iffeature_size) {
             tmp = realloc($3.refine->iffeature, $3.refine->iffeature_size * sizeof *$3.refine->iffeature);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             $3.refine->iffeature = tmp;
           }

           if ($3.refine->must_size) {
             tmp = realloc($3.refine->must, $3.refine->must_size * sizeof *$3.refine->must);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             $3.refine->must = tmp;
           }

           if ($3.refine->dflt_size) {
             tmp = realloc($3.refine->dflt, $3.refine->dflt_size * sizeof *$3.refine->dflt);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             $3.refine->dflt = tmp;
           }
         }

refine_body_opt_stmts: @EMPTYDIR@ { $$.refine = actual;
                                    actual_type = REFINE_KEYWORD;
                                  }
  |  refine_body_opt_stmts must_stmt stmtsep { actual = $1.refine;
                                               actual_type = REFINE_KEYWORD;
                                               if ($1.refine->target_type) {
                                                 if ($1.refine->target_type & (LYS_LEAF | LYS_LIST | LYS_LEAFLIST | LYS_CONTAINER | LYS_ANYXML)) {
                                                   $1.refine->target_type &= (LYS_LEAF | LYS_LIST | LYS_LEAFLIST | LYS_CONTAINER | LYS_ANYXML);
                                                 } else {
                                                   LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, "must", "refine");
                                                   LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid refine target nodetype for the substatements.");
                                                   YYABORT;
                                                 }
                                               } else {
                                                 $1.refine->target_type = LYS_LEAF | LYS_LIST | LYS_LEAFLIST | LYS_CONTAINER | LYS_ANYXML;
                                               }
                                             }
  |  refine_body_opt_stmts if_feature_stmt
     stmtsep { /* leaf, leaf-list, list, container or anyxml */
               /* check possibility of statements combination */
               if ($1.refine->target_type) {
                 if ($1.refine->target_type & (LYS_LEAF | LYS_LIST | LYS_LEAFLIST | LYS_CONTAINER | LYS_ANYDATA)) {
                   $1.refine->target_type &= (LYS_LEAF | LYS_LIST | LYS_LEAFLIST | LYS_CONTAINER | LYS_ANYDATA);
                 } else {
                   free(s);
                   LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, "if-feature", "refine");
                   LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid refine target nodetype for the substatements.");
                   YYABORT;
                 }
               } else {
                 $1.refine->target_type = LYS_LEAF | LYS_LIST | LYS_LEAFLIST | LYS_CONTAINER | LYS_ANYDATA;
               }
             }
  |  refine_body_opt_stmts presence_stmt { if ($1.refine->target_type) {
                                             if ($1.refine->target_type & LYS_CONTAINER) {
                                               if ($1.refine->mod.presence) {
                                                 LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "presence", "refine");
                                                 free(s);
                                                 YYABORT;
                                               }
                                               $1.refine->target_type = LYS_CONTAINER;
                                               $1.refine->mod.presence = lydict_insert_zc(trg->ctx, s);
                                             } else {
                                               free(s);
                                               LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, "presence", "refine");
                                               LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid refine target nodetype for the substatements.");
                                               YYABORT;
                                             }
                                           } else {
                                             $1.refine->target_type = LYS_CONTAINER;
                                             $1.refine->mod.presence = lydict_insert_zc(trg->ctx, s);
                                           }
                                           s = NULL;
                                           $$ = $1;
                                         }
  |  refine_body_opt_stmts default_stmt { int i;

                                          if ($1.refine->dflt_size) {
                                            if (trg->version < 2) {
                                              LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "default", "refine");
                                              YYABORT;
                                            }
                                            if ($1.refine->target_type & LYS_LEAFLIST) {
                                              $1.refine->target_type = LYS_LEAFLIST;
                                            } else {
                                              free(s);
                                              LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, "default", "refine");
                                              LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid refine target nodetype for the substatements.");
                                              YYABORT;
                                            }
                                          } else {
                                            if ($1.refine->target_type) {
                                              if (trg->version < 2 && ($1.refine->target_type & (LYS_LEAF | LYS_CHOICE))) {
                                                $1.refine->target_type &= (LYS_LEAF | LYS_CHOICE);
                                              } if (trg->version > 1 && ($1.refine->target_type & (LYS_LEAF | LYS_LEAFLIST | LYS_CHOICE))) {
                                                /* YANG 1.1 */
                                                $1.refine->target_type &= (LYS_LEAF | LYS_LEAFLIST | LYS_CHOICE);
                                              } else {
                                                free(s);
                                                LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, "default", "refine");
                                                LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid refine target nodetype for the substatements.");
                                                YYABORT;
                                              }
                                            } else {
                                              if (trg->version < 2) {
                                                $1.refine->target_type = LYS_LEAF | LYS_CHOICE;
                                              } else {
                                                /* YANG 1.1 */
                                                $1.refine->target_type = LYS_LEAF | LYS_LEAFLIST | LYS_CHOICE;
                                              }
                                            }
                                          }
                                          /* check for duplicity */
                                          for (i = 0; i < $1.refine->dflt_size; ++i) {
                                              if (ly_strequal($1.refine->dflt[i], s, 0)) {
                                                  LOGVAL(LYE_INARG, LY_VLOG_NONE, NULL, s, "default");
                                                  LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "Duplicated default value \"%s\".", s);
                                                  YYABORT;
                                              }
                                          }
                                          YANG_ADDELEM($1.refine->dflt, $1.refine->dflt_size);
                                          *((const char **)actual) = lydict_insert_zc(trg->ctx, s);
                                          actual = $1.refine;
                                          s = NULL;
                                          $$ = $1;
                                        }
  |  refine_body_opt_stmts config_stmt { if ($1.refine->target_type) {
                                           if ($1.refine->target_type & (LYS_LEAF | LYS_CHOICE | LYS_LIST | LYS_CONTAINER | LYS_LEAFLIST)) {
                                             $1.refine->target_type &= (LYS_LEAF | LYS_CHOICE | LYS_LIST | LYS_CONTAINER | LYS_LEAFLIST);
                                             if ($1.refine->flags & LYS_CONFIG_MASK) {
                                               LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "config", "refine");
                                               YYABORT;
                                             }
                                             $1.refine->flags |= $2;
                                           } else {
                                             LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, "config", "refine");
                                             LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid refine target nodetype for the substatements.");
                                             YYABORT;
                                           }
                                         } else {
                                           $1.refine->target_type = LYS_LEAF | LYS_CHOICE | LYS_LIST | LYS_CONTAINER | LYS_LEAFLIST;
                                           $1.refine->flags |= $2;
                                         }
                                         $$ = $1;
                                       }
  |  refine_body_opt_stmts mandatory_stmt { if ($1.refine->target_type) {
                                              if ($1.refine->target_type & (LYS_LEAF | LYS_CHOICE | LYS_ANYXML)) {
                                                $1.refine->target_type &= (LYS_LEAF | LYS_CHOICE | LYS_ANYXML);
                                                if ($1.refine->flags & LYS_MAND_MASK) {
                                                  LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "mandatory", "refine");
                                                  YYABORT;
                                                }
                                                $1.refine->flags |= $2;
                                              } else {
                                                LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, "mandatory", "refine");
                                                LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid refine target nodetype for the substatements.");
                                                YYABORT;
                                              }
                                            } else {
                                              $1.refine->target_type = LYS_LEAF | LYS_CHOICE | LYS_ANYXML;
                                              $1.refine->flags |= $2;
                                            }
                                            $$ = $1;
                                          }
  |  refine_body_opt_stmts min_elements_stmt { if ($1.refine->target_type) {
                                                 if ($1.refine->target_type & (LYS_LIST | LYS_LEAFLIST)) {
                                                   $1.refine->target_type &= (LYS_LIST | LYS_LEAFLIST);
                                                   if ($1.refine->flags & LYS_RFN_MINSET) {
                                                     LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "min-elements", "refine");
                                                     YYABORT;
                                                   }
                                                   $1.refine->flags |= LYS_RFN_MINSET;
                                                   $1.refine->mod.list.min = $2;
                                                 } else {
                                                   LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, "min-elements", "refine");
                                                   LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid refine target nodetype for the substatements.");
                                                   YYABORT;
                                                 }
                                               } else {
                                                 $1.refine->target_type = LYS_LIST | LYS_LEAFLIST;
                                                 $1.refine->flags |= LYS_RFN_MINSET;
                                                 $1.refine->mod.list.min = $2;
                                               }
                                               $$ = $1;
                                             }
  |  refine_body_opt_stmts max_elements_stmt { if ($1.refine->target_type) {
                                                 if ($1.refine->target_type & (LYS_LIST | LYS_LEAFLIST)) {
                                                   $1.refine->target_type &= (LYS_LIST | LYS_LEAFLIST);
                                                   if ($1.refine->flags & LYS_RFN_MAXSET) {
                                                     LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "max-elements", "refine");
                                                     YYABORT;
                                                   }
                                                   $1.refine->flags |= LYS_RFN_MAXSET;
                                                   $1.refine->mod.list.max = $2;
                                                 } else {
                                                   LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, "max-elements", "refine");
                                                   LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid refine target nodetype for the substatements.");
                                                   YYABORT;
                                                 }
                                               } else {
                                                 $1.refine->target_type = LYS_LIST | LYS_LEAFLIST;
                                                 $1.refine->flags |= LYS_RFN_MAXSET;
                                                 $1.refine->mod.list.max = $2;
                                               }
                                               $$ = $1;
                                             }
  |  refine_body_opt_stmts description_stmt { if (yang_read_description(trg, $1.refine, s, "refine", NODE)) {
                                                YYABORT;
                                              }
                                              s = NULL;
                                            }
  |  refine_body_opt_stmts reference_stmt { if (yang_read_reference(trg, $1.refine, s, "refine", NODE)) {
                                              YYABORT;
                                            }
                                            s = NULL;
                                          }

uses_augment_arg_str: descendant_schema_nodeid optsep
  |  string_1
  ;

uses_augment_arg: uses_augment_arg_str { void *parent;

                                         $$.token = actual_type;
                                         $$.actual = actual;
                                         parent = actual;
                                         YANG_ADDELEM(((struct lys_node_uses *)actual)->augment,
                                                      ((struct lys_node_uses *)actual)->augment_size);
                                         if (yang_read_augment(trg, parent, actual, s)) {
                                           YYABORT;
                                         }
                                         data_node = actual;
                                         s = NULL;
                                         actual_type = AUGMENT_KEYWORD;
                                       }

uses_augment_stmt: AUGMENT_KEYWORD sep uses_augment_arg
                   '{' stmtsep
                       augment_opt_stmt
                   '}' { LOGDBG(LY_LDGYANG, "finished parsing augment statement \"%s\"", data_node->name);
                         actual_type = $3.token;
                         actual = $3.actual;
                         data_node = $3.actual;
                       }

augment_arg_str: absolute_schema_nodeids optsep
  |  string_1

augment_arg: augment_arg_str { $$.token = actual_type;
                               $$.actual = actual;
                               YANG_ADDELEM(trg->augment, trg->augment_size);
                               if (yang_read_augment(trg, NULL, actual, s)) {
                                 YYABORT;
                               }
                               data_node = actual;
                               s = NULL;
                               actual_type = AUGMENT_KEYWORD;
                             }

augment_stmt: AUGMENT_KEYWORD sep augment_arg
              '{' stmtsep
                  augment_opt_stmt
              '}' { LOGDBG(LY_LDGYANG, "finished parsing augment statement \"%s\"", data_node->name);
                    actual_type = $3.token;
                    actual = $3.actual;
                    data_node = $3.actual;
                  }

augment_opt_stmt: @EMPTYDIR@ { $$.augment = actual; }
  |  augment_opt_stmt when_stmt stmtsep
  |  augment_opt_stmt if_feature_stmt stmtsep
  |  augment_opt_stmt status_stmt { if ($1.augment->flags & LYS_STATUS_MASK) {
                                      LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.augment, "status", "augment");
                                      YYABORT;
                                    }
                                    $1.augment->flags |= $2;
                                  }
  |  augment_opt_stmt description_stmt { if (yang_read_description(trg, $1.augment, s, "augment", NODE_PRINT)) {
                                           YYABORT;
                                         }
                                         s = NULL;
                                       }
  |  augment_opt_stmt reference_stmt { if (yang_read_reference(trg, $1.augment, s, "augment", NODE_PRINT)) {
                                         YYABORT;
                                       }
                                       s = NULL;
                                     }
  |  augment_opt_stmt data_def_stmt stmtsep
  |  augment_opt_stmt action_stmt stmtsep
  |  augment_opt_stmt notification_stmt stmtsep { if (trg->version < 2) {
                                                    LOGVAL(LYE_INSTMT, LY_VLOG_LYS, $1.augment, "notification");
                                                    YYABORT;
                                                  }
                                                }
  |  augment_opt_stmt case_stmt stmtsep

action_arg_str: identifier_arg_str { if (param->module->version != 2) {
                                       LOGVAL(LYE_INSTMT, LY_VLOG_LYS, actual, "action");
                                       free(s);
                                       YYABORT;
                                     }
                                     $$.token = actual_type;
                                     $$.actual = actual;
                                     if (!(actual = yang_read_node(trg, actual, param->node, s, LYS_ACTION, sizeof(struct lys_node_rpc_action)))) {
                                       YYABORT;
                                     }
                                     data_node = actual;
                                     s = NULL;
                                     actual_type = ACTION_KEYWORD;
                                   }

action_stmt: ACTION_KEYWORD sep action_arg_str rpc_end
             { LOGDBG(LY_LDGYANG, "finished parsing action statement \"%s\"", data_node->name);
               actual_type = $3.token;
               actual = $3.actual;
               data_node = $3.actual;
             }

rpc_arg_str: identifier_arg_str { $$.token = actual_type;
                                  $$.actual = actual;
                                  if (!(actual = yang_read_node(trg, NULL, param->node, s, LYS_RPC, sizeof(struct lys_node_rpc_action)))) {
                                    YYABORT;
                                  }
                                  data_node = actual;
                                  s = NULL;
                                  actual_type = RPC_KEYWORD;
                                }

rpc_stmt: RPC_KEYWORD sep rpc_arg_str rpc_end
          { LOGDBG(LY_LDGYANG, "finished parsing rpc statement \"%s\"", data_node->name);
            actual_type = $3.token;
            actual = $3.actual;
            data_node = $3.actual;
          }

rpc_end: ';'
  |  '{' stmtsep
         rpc_opt_stmt
      '}' { void *tmp;

            if ($3.node.ptr_rpc->iffeature_size) {
              tmp = realloc($3.node.ptr_rpc->iffeature, $3.node.ptr_rpc->iffeature_size * sizeof *$3.node.ptr_rpc->iffeature);
              if (!tmp) {
                LOGMEM;
                YYABORT;
              }
              $3.node.ptr_rpc->iffeature = tmp;
            }

            if ($3.node.ptr_rpc->tpdf_size) {
              tmp = realloc($3.node.ptr_rpc->tpdf, $3.node.ptr_rpc->tpdf_size * sizeof *$3.node.ptr_rpc->tpdf);
              if (!tmp) {
                LOGMEM;
                YYABORT;
              }
              $3.node.ptr_rpc->tpdf = tmp;
            }
          }


rpc_opt_stmt: @EMPTYDIR@ { $$.node.ptr_rpc = actual;
                           $$.node.flag = 0;
                         }
  |  rpc_opt_stmt if_feature_stmt stmtsep
  |  rpc_opt_stmt status_stmt { if ($1.node.ptr_rpc->flags & LYS_STATUS_MASK) {
                                  LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_rpc, "status", "rpc");
                                  YYABORT;
                                }
                                $1.node.ptr_rpc->flags |= $2;
                             }
  |  rpc_opt_stmt description_stmt { if (yang_read_description(trg, $1.node.ptr_rpc, s, "rpc", NODE_PRINT)) {
                                       YYABORT;
                                     }
                                     s = NULL;
                                   }
  |  rpc_opt_stmt reference_stmt { if (yang_read_reference(trg, $1.node.ptr_rpc, s, "rpc", NODE_PRINT)) {
                                     YYABORT;
                                   }
                                   s = NULL;
                                 }
  |  rpc_opt_stmt typedef_stmt stmtsep
  |  rpc_opt_stmt grouping_stmt stmtsep
  |  rpc_opt_stmt input_stmt stmtsep { if ($1.node.flag & LYS_RPC_INPUT) {
                                         LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_rpc, "input", "rpc");
                                         YYABORT;
                                       }
                                       $1.node.flag |= LYS_RPC_INPUT;
                                       $$ = $1;
                                     }
  |  rpc_opt_stmt output_stmt stmtsep { if ($1.node.flag & LYS_RPC_OUTPUT) {
                                          LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.node.ptr_rpc, "output", "rpc");
                                          YYABORT;
                                        }
                                        $1.node.flag |= LYS_RPC_OUTPUT;
                                        $$ = $1;
                                      }

input_arg: INPUT_KEYWORD optsep { $$.token = actual_type;
                                  $$.actual = actual;
                                  s = strdup("input");
                                  if (!s) {
                                    LOGMEM;
                                    YYABORT;
                                  }
                                  if (!(actual = yang_read_node(trg, actual, param->node, s, LYS_INPUT, sizeof(struct lys_node_inout)))) {
                                    YYABORT;
                                  }
                                  data_node = actual;
                                  s = NULL;
                                  actual_type = INPUT_KEYWORD;
                                }

input_stmt: input_arg
            '{' stmtsep
                input_output_opt_stmt
            '}' { void *tmp;
                  struct lys_node_inout *input = actual;

                  if (input->must_size) {
                    tmp = realloc(input->must, input->must_size * sizeof *input->must);
                    if (!tmp) {
                      LOGMEM;
                      YYABORT;
                    }
                    input->must = tmp;
                  }

                  if (input->tpdf_size) {
                    tmp = realloc(input->tpdf, input->tpdf_size * sizeof *input->tpdf);
                    if (!tmp) {
                      LOGMEM;
                      YYABORT;
                    }
                    input->tpdf = tmp;
                  }

                  LOGDBG(LY_LDGYANG, "finished parsing input statement \"%s\"", data_node->name);
                  actual_type = $1.token;
                  actual = $1.actual;
                  data_node = $1.actual;
                }

input_output_opt_stmt: @EMPTYDIR@
  |  input_output_opt_stmt must_stmt stmtsep
  |  input_output_opt_stmt typedef_stmt stmtsep
  |  input_output_opt_stmt grouping_stmt stmtsep
  |  input_output_opt_stmt data_def_stmt stmtsep

output_arg: OUTPUT_KEYWORD optsep { $$.token = actual_type;
                                    $$.actual = actual;
                                    s = strdup("output");
                                    if (!s) {
                                      LOGMEM;
                                      YYABORT;
                                    }
                                    if (!(actual = yang_read_node(trg, actual, param->node, s, LYS_OUTPUT, sizeof(struct lys_node_inout)))) {
                                      YYABORT;
                                    }
                                    data_node = actual;
                                    s = NULL;
                                    actual_type = OUTPUT_KEYWORD;
                                  }

output_stmt: output_arg
             '{' stmtsep
                 input_output_opt_stmt
             '}' { void *tmp;
                   struct lys_node_inout *output = actual;

                   if (output->must_size) {
                     tmp = realloc(output->must, output->must_size * sizeof *output->must);
                     if (!tmp) {
                       LOGMEM;
                       YYABORT;
                     }
                     output->must = tmp;
                   }

                   if (output->tpdf_size) {
                     tmp = realloc(output->tpdf, output->tpdf_size * sizeof *output->tpdf);
                     if (!tmp) {
                       LOGMEM;
                       YYABORT;
                     }
                     output->tpdf = tmp;
                   }

                   LOGDBG(LY_LDGYANG, "finished parsing output statement \"%s\"", data_node->name);
                   actual_type = $1.token;
                   actual = $1.actual;
                   data_node = $1.actual;
                 }

notification_arg_str: identifier_arg_str { $$.token = actual_type;
                                           $$.actual = actual;
                                           if (!(actual = yang_read_node(trg, actual, param->node, s, LYS_NOTIF, sizeof(struct lys_node_notif)))) {
                                             YYABORT;
                                           }
                                           data_node = actual;
                                           actual_type = NOTIFICATION_KEYWORD;
                                         }

notification_stmt: NOTIFICATION_KEYWORD sep notification_arg_str notification_end
                   { LOGDBG(LY_LDGYANG, "finished parsing notification statement \"%s\"", data_node->name);
                     actual_type = $3.token;
                     actual = $3.actual;
                     data_node = $3.actual;
                   }

notification_end: ';'
  |  '{' stmtsep
         notification_opt_stmt
      '}' { void *tmp;

            if ($3.notif->must_size) {
              tmp = realloc($3.notif->must, $3.notif->must_size * sizeof *$3.notif->must);
              if (!tmp) {
                LOGMEM;
                YYABORT;
              }
              $3.notif->must = tmp;
            }

           if ($3.notif->iffeature_size) {
             tmp = realloc($3.notif->iffeature, $3.notif->iffeature_size * sizeof *$3.notif->iffeature);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             $3.notif->iffeature = tmp;
           }

           if ($3.notif->tpdf_size) {
             tmp = realloc($3.notif->tpdf, $3.notif->tpdf_size * sizeof *$3.notif->tpdf);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             $3.notif->tpdf = tmp;
           }
          }

notification_opt_stmt: @EMPTYDIR@ { $$.notif = actual; }
  |  notification_opt_stmt must_stmt stmtsep
  |  notification_opt_stmt if_feature_stmt stmtsep
  |  notification_opt_stmt status_stmt { if ($1.notif->flags & LYS_STATUS_MASK) {
                                           LOGVAL(LYE_TOOMANY, LY_VLOG_LYS, $1.notif, "status", "notification");
                                           YYABORT;
                                         }
                                         $1.notif->flags |= $2;
                                       }
  |  notification_opt_stmt description_stmt { if (yang_read_description(trg, $1.notif, s, "notification", NODE_PRINT)) {
                                                YYABORT;
                                              }
                                              s = NULL;
                                            }
  |  notification_opt_stmt reference_stmt { if (yang_read_reference(trg, $1.notif, s, "notification", NODE_PRINT)) {
                                              YYABORT;
                                            }
                                            s = NULL;
                                          }
  |  notification_opt_stmt typedef_stmt stmtsep
  |  notification_opt_stmt grouping_stmt stmtsep
  |  notification_opt_stmt data_def_stmt stmtsep

deviation_arg: deviation_arg_str { $$.token = actual_type;
                                   $$.actual = actual;
                                   YANG_ADDELEM(trg->deviation, trg->deviation_size);
                                   ((struct lys_deviation *)actual)->target_name = transform_schema2json(trg, s);
                                   free(s);
                                   if (!((struct lys_deviation *)actual)->target_name) {
                                     YYABORT;
                                   }
                                   s = NULL;
                                   actual_type = DEVIATION_KEYWORD;
                                 }

deviation_stmt: DEVIATION_KEYWORD sep deviation_arg
                '{' stmtsep
                    deviation_opt_stmt
                '}' { void *tmp;

                      if ($6->deviate_size) {
                        tmp = realloc($6->deviate, $6->deviate_size * sizeof *$6->deviate);
                        if (!tmp) {
                          LOGINT;
                          YYABORT;
                        }
                        $6->deviate = tmp;
                      } else {
                        LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, "deviate", "deviation");
                        YYABORT;
                      }
                      actual_type = $3.token;
                      actual = $3.actual;
                    }

deviation_opt_stmt: @EMPTYDIR@ { $$ = actual; }
  |  deviation_opt_stmt description_stmt { if (yang_read_description(trg, $1, s, "deviation", NODE)) {
                                             YYABORT;
                                           }
                                           s = NULL;
                                           $$ = $1;
                                         }
  |  deviation_opt_stmt reference_stmt { if (yang_read_reference(trg, $1, s, "deviation", NODE)) {
                                           YYABORT;
                                         }
                                         s = NULL;
                                         $$ = $1;
                                       }
  |  deviation_opt_stmt DEVIATE_KEYWORD sep deviate_body_stmt stmtsep

deviation_arg_str: absolute_schema_nodeids optsep
  | string_1

deviate_body_stmt: deviate_not_supported_stmt
  |  deviate_stmts


deviate_not_supported: NOT_SUPPORTED_KEYWORD { $$.token = actual_type;
                                               $$.actual = actual;
                                               if (!(actual = yang_read_deviate_unsupported(actual))) {
                                                 YYABORT;
                                               }
                                               actual_type = NOT_SUPPORTED_KEYWORD;
                                             }

deviate_not_supported_stmt: deviate_not_supported optsep deviate_not_supported_end
                            { actual_type = $1.token;
                              actual = $1.actual;
                            }

deviate_not_supported_end: ';'
  | '{' stmtsep '}'

deviate_stmts: deviate_add_stmt
  |  deviate_replace_stmt
  |  deviate_delete_stmt

deviate_add: ADD_KEYWORD { $$.token = actual_type;
                           $$.actual = actual;
                           if (!(actual = yang_read_deviate(actual, LY_DEVIATE_ADD))) {
                             YYABORT;
                           }
                           actual_type = ADD_KEYWORD;
                         }

deviate_add_stmt: deviate_add optsep deviate_add_end
                  { actual_type = $1.token;
                    actual = $1.actual;
                  }

deviate_add_end: ';'
  |  '{' stmtsep
         deviate_add_opt_stmt
     '}' { void *tmp;

           if ($3->must_size) {
             tmp = realloc($3->must, $3->must_size * sizeof *$3->must);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             $3->must = tmp;
           }

           if ($3->unique_size) {
             tmp = realloc($3->unique, $3->unique_size * sizeof *$3->unique);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             $3->unique = tmp;
           }

           if ($3->dflt_size) {
             tmp = realloc($3->dflt, $3->dflt_size * sizeof *$3->dflt);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             $3->dflt = tmp;
           }
         }

deviate_add_opt_stmt: @EMPTYDIR@ { $$ = actual; }
  |  deviate_add_opt_stmt units_stmt { if (yang_read_units(trg, actual, s, ADD_KEYWORD)) {
                                         YYABORT;
                                       }
                                       s = NULL;
                                       $$ = $1;
                                     }
  |  deviate_add_opt_stmt must_stmt stmtsep
  |  deviate_add_opt_stmt unique_stmt { YANG_ADDELEM($1->unique, $1->unique_size);
                                        ((struct lys_unique *)actual)->expr = (const char **)s;
                                        s = NULL;
                                        actual = $1;
                                        $$= $1;
                                      }
  |  deviate_add_opt_stmt default_stmt { YANG_ADDELEM($1->dflt, $1->dflt_size);
                                         *((const char **)actual) = lydict_insert_zc(trg->ctx, s);
                                         s = NULL;
                                         actual = $1;
                                         $$ = $1;
                                       }
  |  deviate_add_opt_stmt config_stmt { if ($1->flags & LYS_CONFIG_MASK) {
                                          LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "config", "deviate");
                                          YYABORT;
                                        }
                                        $1->flags = $2;
                                        $$ = $1;
                                      }
  |  deviate_add_opt_stmt mandatory_stmt { if ($1->flags & LYS_MAND_MASK) {
                                             LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "mandatory", "deviate");
                                             YYABORT;
                                           }
                                           $1->flags = $2;
                                           $$ = $1;
                                         }
  |  deviate_add_opt_stmt min_elements_stmt { if ($1->min_set) {
                                                LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "min-elements", "deviation");
                                                YYABORT;
                                              }
                                              $1->min = $2;
                                              $1->min_set = 1;
                                              $$ =  $1;
                                            }
  |  deviate_add_opt_stmt max_elements_stmt { if ($1->max_set) {
                                                LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "max-elements", "deviation");
                                                YYABORT;
                                              }
                                              $1->max = $2;
                                              $1->max_set = 1;
                                              $$ =  $1;
                                            }

deviate_delete: DELETE_KEYWORD { $$.token = actual_type;
                                 $$.actual = actual;
                                 if (!(actual = yang_read_deviate(actual, LY_DEVIATE_DEL))) {
                                   YYABORT;
                                 }
                                 actual_type = DELETE_KEYWORD;
                               }

deviate_delete_stmt: deviate_delete optsep deviate_delete_end
                     { actual_type = $1.token;
                       actual = $1.actual;
                     }

deviate_delete_end: ';'
  |  '{' stmtsep
         deviate_delete_opt_stmt
      '}' { void *tmp;

            if ($3->must_size) {
              tmp = realloc($3->must, $3->must_size * sizeof *$3->must);
              if (!tmp) {
                LOGMEM;
                YYABORT;
              }
              $3->must = tmp;
            }

            if ($3->unique_size) {
              tmp = realloc($3->unique, $3->unique_size * sizeof *$3->unique);
              if (!tmp) {
                LOGMEM;
                YYABORT;
              }
              $3->unique = tmp;
            }

            if ($3->dflt_size) {
              tmp = realloc($3->dflt, $3->dflt_size * sizeof *$3->dflt);
              if (!tmp) {
                LOGMEM;
                YYABORT;
              }
              $3->dflt = tmp;
            }
          }

deviate_delete_opt_stmt: @EMPTYDIR@ { $$ = actual; }
  |  deviate_delete_opt_stmt units_stmt { if (yang_read_units(trg, actual, s, DELETE_KEYWORD)) {
                                            YYABORT;
                                          }
                                          s = NULL;
                                          $$ = $1;
                                        }
  |  deviate_delete_opt_stmt must_stmt stmtsep
  |  deviate_delete_opt_stmt unique_stmt { YANG_ADDELEM($1->unique, $1->unique_size);
                                           ((struct lys_unique *)actual)->expr = (const char **)s;
                                           s = NULL;
                                           actual = $1;
                                           $$ = $1;
                                         }
  |  deviate_delete_opt_stmt default_stmt { YANG_ADDELEM($1->dflt, $1->dflt_size);
                                            *((const char **)actual) = lydict_insert_zc(trg->ctx, s);
                                            s = NULL;
                                            actual = $1;
                                            $$ = $1;
                                          }

deviate_replace: REPLACE_KEYWORD { $$.token = actual_type;
                                   $$.actual = actual;
                                   if (!(actual = yang_read_deviate(actual, LY_DEVIATE_RPL))) {
                                     YYABORT;
                                   }
                                   actual_type = REPLACE_KEYWORD;
                                 }

deviate_replace_stmt: deviate_replace optsep deviate_replace_end
                      { actual_type = $1.token;
                        actual = $1.actual;
                      }

deviate_replace_end: ';'
  |  '{' stmtsep
         deviate_replace_opt_stmt
     '}' { void *tmp;

           if ($3->dflt_size) {
             tmp = realloc($3->dflt, $3->dflt_size * sizeof *$3->dflt);
             if (!tmp) {
               LOGMEM;
               YYABORT;
             }
             $3->dflt = tmp;
           }
         }

deviate_replace_opt_stmt: @EMPTYDIR@ { $$ = actual; }
  |  deviate_replace_opt_stmt type_stmt stmtsep
  |  deviate_replace_opt_stmt units_stmt { if (yang_read_units(trg, actual, s, DELETE_KEYWORD)) {
                                             YYABORT;
                                           }
                                           s = NULL;
                                           $$ = $1;
                                         }
  |  deviate_replace_opt_stmt default_stmt { YANG_ADDELEM($1->dflt, $1->dflt_size);
                                             *((const char **)actual) = lydict_insert_zc(trg->ctx, s);
                                             s = NULL;
                                             actual = $1;
                                             $$ = $1;
                                           }
  |  deviate_replace_opt_stmt config_stmt { if ($1->flags & LYS_CONFIG_MASK) {
                                              LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "config", "deviate");
                                              YYABORT;
                                            }
                                            $1->flags = $2;
                                            $$ = $1;
                                          }
  |  deviate_replace_opt_stmt mandatory_stmt { if ($1->flags & LYS_MAND_MASK) {
                                                 LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "mandatory", "deviate");
                                                 YYABORT;
                                               }
                                               $1->flags = $2;
                                               $$ = $1;
                                             }
  |  deviate_replace_opt_stmt min_elements_stmt { if ($1->min_set) {
                                                    LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "min-elements", "deviation");
                                                    YYABORT;
                                                  }
                                                  $1->min = $2;
                                                  $1->min_set = 1;
                                                  $$ =  $1;
                                                }
  |  deviate_replace_opt_stmt max_elements_stmt { if ($1->max_set) {
                                                    LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "max-elements", "deviation");
                                                    YYABORT;
                                                  }
                                                  $1->max = $2;
                                                  $1->max_set = 1;
                                                  $$ =  $1;
                                                }

when_arg_str: string  { $$.token = actual_type;
                        $$.actual = actual;
                        if (!(actual = yang_read_when(trg, actual, actual_type, s))) {
                          YYABORT;
                        }
                        s = NULL;
                        actual_type = WHEN_KEYWORD;
                      }

when_stmt: WHEN_KEYWORD sep when_arg_str when_end
           { actual_type = $3.token;
             actual = $3.actual;
           }

when_end: ';'
  |  '{' stmtsep
         when_opt_stmt
     '}'

when_opt_stmt: @EMPTYDIR@
  |  when_opt_stmt description_stmt { if (yang_read_description(trg, actual, s, "when", NODE)) {
                                        YYABORT;
                                      }
                                      s = NULL;
                                    }
  |  when_opt_stmt reference_stmt { if (yang_read_reference(trg, actual, s, "when", NODE)) {
                                      YYABORT;
                                    }
                                    s = NULL;
                                  }

config_arg: config_arg_str { $$ = $1;
                             backup_type = actual_type;
                             actual_type = CONFIG_KEYWORD;
                           }

config_stmt: CONFIG_KEYWORD sep config_arg stmtend { $$ = $3; }

config_arg_str: TRUE_KEYWORD optsep { $$ = LYS_CONFIG_W | LYS_CONFIG_SET; }
  |  FALSE_KEYWORD optsep { $$ = LYS_CONFIG_R | LYS_CONFIG_SET; }
  |  string_1 { if (!strcmp(s, "true")) {
                  $$ = LYS_CONFIG_W | LYS_CONFIG_SET;
                } else if (!strcmp(s, "false")) {
                  $$ = LYS_CONFIG_R | LYS_CONFIG_SET;
                } else {
                  LOGVAL(LYE_INARG, LY_VLOG_NONE, NULL, s, "config");
                  free(s);
                  YYABORT;
                }
                free(s);
                s = NULL;
              }

mandatory_arg: mandatory_arg_str { $$ = $1;
                                   backup_type = actual_type;
                                   actual_type = MANDATORY_KEYWORD;
                                 }

mandatory_stmt: MANDATORY_KEYWORD sep mandatory_arg stmtend { $$ = $3; }

mandatory_arg_str: TRUE_KEYWORD optsep { $$ = LYS_MAND_TRUE; }
  |  FALSE_KEYWORD optsep { $$ = LYS_MAND_FALSE; }
  |  string_1 { if (!strcmp(s, "true")) {
                  $$ = LYS_MAND_TRUE;
                } else if (!strcmp(s, "false")) {
                  $$ = LYS_MAND_FALSE;
                } else {
                  LOGVAL(LYE_INARG, LY_VLOG_NONE, NULL, s, "mandatory");
                  free(s);
                  YYABORT;
                }
                free(s);
                s = NULL;
              }

presence_arg: string { backup_type = actual_type;
                       actual_type = PRESENCE_KEYWORD;
                     }

presence_stmt: PRESENCE_KEYWORD sep presence_arg stmtend

min_value_arg: min_value_arg_str { $$ = $1;
                                   backup_type = actual_type;
                                   actual_type = MIN_ELEMENTS_KEYWORD;
                                 }

min_elements_stmt: MIN_ELEMENTS_KEYWORD sep min_value_arg stmtend { $$ = $3; }

min_value_arg_str: non_negative_integer_value optsep { $$ = $1; }
  |  string_1 { if (strlen(s) == 1 && s[0] == '0') {
                  $$ = 0;
                } else {
                  /* convert it to uint32_t */
                  uint64_t val;
                  char *endptr = NULL;
                  errno = 0;

                  val = strtoul(s, &endptr, 10);
                  if (*endptr || s[0] == '-' || errno || val > UINT32_MAX) {
                      LOGVAL(LYE_INARG, LY_VLOG_NONE, NULL, s, "min-elements");
                      free(s);
                      YYABORT;
                  }
                  $$ = (uint32_t) val;
                }
                free(s);
                s = NULL;
              }

max_value_arg: max_value_arg_str { $$ = $1;
                                   backup_type = actual_type;
                                   actual_type = MAX_ELEMENTS_KEYWORD;
                                 }

max_elements_stmt: MAX_ELEMENTS_KEYWORD sep max_value_arg stmtend { $$ = $3; }

max_value_arg_str: UNBOUNDED_KEYWORD optsep { $$ = 0; }
  |  positive_integer_value optsep { $$ = $1; }
  |  string_1 { if (!strcmp(s, "unbounded")) {
                  $$ = 0;
                } else {
                  /* convert it to uint32_t */
                  uint64_t val;
                  char *endptr = NULL;
                  errno = 0;

                  val = strtoul(s, &endptr, 10);
                  if (*endptr || s[0] == '-' || errno || val == 0 || val > UINT32_MAX) {
                      LOGVAL(LYE_INARG, LY_VLOG_NONE, NULL, s, "max-elements");
                      free(s);
                      YYABORT;
                  }
                  $$ = (uint32_t) val;
                }
                free(s);
                s = NULL;
              }

ordered_by_arg: ordered_by_arg_str { $$ = $1;
                                     backup_type = actual_type;
                                     actual_type = ORDERED_BY_KEYWORD;
                                   }

ordered_by_stmt: ORDERED_BY_KEYWORD sep ordered_by_arg stmtend { $$ = $3; }

ordered_by_arg_str: USER_KEYWORD optsep { $$ = LYS_USERORDERED; }
  |  SYSTEM_KEYWORD optsep { $$ = LYS_SYSTEMORDERED; }
  |  string_1 { if (!strcmp(s, "user")) {
                  $$ = LYS_USERORDERED;
                } else if (!strcmp(s, "system")) {
                  $$ = LYS_SYSTEMORDERED;
                } else {
                  free(s);
                  YYABORT;
                }
                free(s);
                s=NULL;
              }

must_agr_str: string { $$.token = actual_type;
                       $$.actual = actual;
                       switch (actual_type) {
                       case CONTAINER_KEYWORD:
                         YANG_ADDELEM(((struct lys_node_container *)actual)->must,
                                     ((struct lys_node_container *)actual)->must_size);
                         break;
                       case ANYDATA_KEYWORD:
                       case ANYXML_KEYWORD:
                         YANG_ADDELEM(((struct lys_node_anydata *)actual)->must,
                                     ((struct lys_node_anydata *)actual)->must_size);
                         break;
                       case LEAF_KEYWORD:
                         YANG_ADDELEM(((struct lys_node_leaf *)actual)->must,
                                     ((struct lys_node_leaf *)actual)->must_size);
                         break;
                       case LEAF_LIST_KEYWORD:
                         YANG_ADDELEM(((struct lys_node_leaflist *)actual)->must,
                                     ((struct lys_node_leaflist *)actual)->must_size);
                         break;
                       case LIST_KEYWORD:
                         YANG_ADDELEM(((struct lys_node_list *)actual)->must,
                                     ((struct lys_node_list *)actual)->must_size);
                         break;
                       case REFINE_KEYWORD:
                         YANG_ADDELEM(((struct lys_refine *)actual)->must,
                                     ((struct lys_refine *)actual)->must_size);
                         break;
                       case ADD_KEYWORD:
                       case DELETE_KEYWORD:
                         YANG_ADDELEM(((struct lys_deviate *)actual)->must,
                                      ((struct lys_deviate *)actual)->must_size);
                         break;
                       case NOTIFICATION_KEYWORD:
                         if (trg->version < 2) {
                           free(s);
                           LOGVAL(LYE_INSTMT, LY_VLOG_LYS, actual, "must");
                           YYABORT;
                         }
                         YANG_ADDELEM(((struct lys_node_notif *)actual)->must,
                                     ((struct lys_node_notif *)actual)->must_size);
                         break;
                       case INPUT_KEYWORD:
                       case OUTPUT_KEYWORD:
                         if (trg->version < 2) {
                           free(s);
                           LOGVAL(LYE_INSTMT, LY_VLOG_LYS, actual, "must");
                           YYABORT;
                         }
                         YANG_ADDELEM(((struct lys_node_inout *)actual)->must,
                                     ((struct lys_node_inout *)actual)->must_size);
                         break;
                       case EXTENSION_INSTANCE:
                         /* must is already allocated */
                         break;
                       default:
                         free(s);
                         LOGINT;
                         YYABORT;
                       }
                       ((struct lys_restr *)actual)->expr = transform_schema2json(trg, s);
                       free(s);
                       if (!((struct lys_restr *)actual)->expr) {
                         YYABORT;
                       }
                       s = NULL;
                       actual_type = MUST_KEYWORD;
                     }

must_stmt: MUST_KEYWORD sep must_agr_str must_end
           { actual_type = $3.token;
             actual = $3.actual;
           }

must_end: ';'
  |  '{' stmtsep
         message_opt_stmt
     '}'

unique_arg: unique_arg_str { backup_type = actual_type;
                             actual_type = UNIQUE_KEYWORD;
                           }

unique_stmt: UNIQUE_KEYWORD sep unique_arg stmtend

unique_arg_str: descendant_schema_nodeid optsep
  |  string_1

key_arg: key_arg_str { backup_type = actual_type;
                       actual_type = KEY_KEYWORD;
                     }

key_stmt: KEY_KEYWORD sep key_arg stmtend;

key_arg_str: node_identifier { s = strdup(yyget_text(scanner));
                               if (!s) {
                                 LOGMEM;
                                 YYABORT;
                               }
                             }
             optsep
  |  string_1
  ;

range_arg_str: string { $$.token = actual_type;
                        $$.actual = actual;
                        if (!(actual = yang_read_range(trg, actual, s, is_ext_instance))) {
                          YYABORT;
                        }
                        actual_type = RANGE_KEYWORD;
                        s = NULL;
                      }

absolute_schema_nodeid: '/' node_identifier { if (s) {
                                                s = ly_realloc(s,strlen(s) + yyget_leng(scanner) + 2);
                                                if (!s) {
                                                  LOGMEM;
                                                  YYABORT;
                                                }
                                                strcat(s,"/");
                                                strcat(s, yyget_text(scanner));
                                              } else {
                                                s = malloc(yyget_leng(scanner) + 2);
                                                if (!s) {
                                                  LOGMEM;
                                                  YYABORT;
                                                }
                                                s[0]='/';
                                                memcpy(s + 1, yyget_text(scanner), yyget_leng(scanner) + 1);
                                              }
                                            }

absolute_schema_nodeids: absolute_schema_nodeid absolute_schema_nodeid_opt;

absolute_schema_nodeid_opt: @EMPTYDIR@
  |  absolute_schema_nodeid_opt absolute_schema_nodeid
  ;

descendant_schema_nodeid: node_identifier { if (s) {
                                              s = ly_realloc(s,strlen(s) + yyget_leng(scanner) + 1);
                                              if (!s) {
                                                LOGMEM;
                                                YYABORT;
                                              }
                                              strcat(s, yyget_text(scanner));
                                            } else {
                                              s = strdup(yyget_text(scanner));
                                              if (!s) {
                                                LOGMEM;
                                                YYABORT;
                                              }
                                            }
                                          }
                          absolute_schema_nodeid_opt;

path_arg_str: { tmp_s = yyget_text(scanner); } absolute_paths { s = strdup(tmp_s);
                                                                if (!s) {
                                                                  LOGMEM;
                                                                  YYABORT;
                                                                }
                                                                s[strlen(s) - 1] = '\0';
                                                             }
  |  { tmp_s = yyget_text(scanner); } relative_path { s = strdup(tmp_s);
                                                      if (!s) {
                                                        LOGMEM;
                                                        YYABORT;
                                                      }
                                                      s[strlen(s) - 1] = '\0';
                                                    }
  |  string_1
  ;

absolute_path: '/' node_identifier path_predicate

absolute_paths: absolute_path absolute_path_opt

absolute_path_opt: @EMPTYDIR@
  |  absolute_path_opt absolute_path;

relative_path: relative_path_part1 relative_path_part1_opt descendant_path

relative_path_part1: DOUBLEDOT '/';

relative_path_part1_opt: @EMPTYDIR@
  |  relative_path_part1_opt relative_path_part1;

descendant_path: node_identifier descendant_path_opt

descendant_path_opt: @EMPTYDIR@
  |  path_predicate absolute_paths;

path_predicate: @EMPTYDIR@
  | path_predicate '[' whitespace_opt path_equality_expr whitespace_opt ']'

path_equality_expr: node_identifier whitespace_opt '=' whitespace_opt path_key_expr

path_key_expr: current_function_invocation whitespace_opt '/' whitespace_opt
                     rel_path_keyexpr

rel_path_keyexpr: rel_path_keyexpr_part1 rel_path_keyexpr_part1_opt
                    node_identifier rel_path_keyexpr_part2
                     node_identifier

rel_path_keyexpr_part1: DOUBLEDOT whitespace_opt '/' whitespace_opt;

rel_path_keyexpr_part1_opt: @EMPTYDIR@
  |  rel_path_keyexpr_part1_opt rel_path_keyexpr_part1;

rel_path_keyexpr_part2: @EMPTYDIR@
  | rel_path_keyexpr_part2 whitespace_opt '/' whitespace_opt node_identifier;

current_function_invocation: CURRENT_KEYWORD whitespace_opt '(' whitespace_opt ')'

positive_integer_value: NON_NEGATIVE_INTEGER { /* convert it to uint32_t */
                                                unsigned long val;

                                                val = strtoul(yyget_text(scanner), NULL, 10);
                                                if (val > UINT32_MAX) {
                                                    LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "Converted number is very long.");
                                                    YYABORT;
                                                }
                                                $$ = (uint32_t) val;
                                             }

non_negative_integer_value: ZERO { $$ = 0; }
  |  positive_integer_value { $$ = $1; }
  ;

integer_value: ZERO { $$ = 0; }
  |  integer_value_convert { /* convert it to int32_t */
                             int64_t val;

                             val = strtoll(yyget_text(scanner), NULL, 10);
                             if (val < INT32_MIN || val > INT32_MAX) {
                                 LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "The number is not in the correct range (INT32_MIN..INT32_MAX): \"%d\"",val);
                                 YYABORT;
                             }
                             $$ = (int32_t) val;
                           }

integer_value_convert: INTEGER
  |  NON_NEGATIVE_INTEGER

prefix_arg_str: string_1
  |  identifiers optsep;

identifier_arg_str: identifiers optsep
  |  string_1 { if (lyp_check_identifier(s, LY_IDENT_SIMPLE, trg, NULL)) {
                    free(s);
                    YYABORT;
                }
              }

node_identifier: identifier
  |  IDENTIFIERPREFIX
  ;

identifier_ref_arg_str: identifiers optsep
  | identifiers_ref optsep
  | string_1 { char *tmp;

               if ((tmp = strchr(s, ':'))) {
                 *tmp = '\0';
                 /* check prefix */
                 if (lyp_check_identifier(s, LY_IDENT_SIMPLE, trg, NULL)) {
                   free(s);
                   YYABORT;
                 }
                 /* check identifier */
                 if (lyp_check_identifier(tmp + 1, LY_IDENT_SIMPLE, trg, NULL)) {
                   free(s);
                   YYABORT;
                 }
                 *tmp = ':';
               } else {
                 /* check identifier */
                 if (lyp_check_identifier(s, LY_IDENT_SIMPLE, trg, NULL)) {
                   free(s);
                   YYABORT;
                 }
               }
             }

stmtend: semicolom stmtsep { s = $1; }
  | curly_bracket_open stmtsep curly_bracket_close stmtsep { s = $1; }


semicolom: ';' { actual_type = backup_type;
                 backup_type = NODE;
                 $$ = s;
                 s = NULL;
               }

curly_bracket_close: '}' { actual_type = backup_type;
                           backup_type = NODE;
                         }

curly_bracket_open: '{' { $$ = s;
                          s = NULL;
                        }


stmtsep: @EMPTYDIR@
  | stmtsep sep_stmt
  | stmtsep unknown_statement
  ;

unknown_statement: identifiers_ref string_opt unknown_statement_end
                   { actual_type = $2.token;
                     actual = $2.actual;
                   }

string_opt: string_opt_part1 string_opt_part2 { $$.token = actual_type;
                                                $$.actual = actual;
                                                if (!(actual = yang_read_ext(trg, (actual) ? actual : trg, $1, s,
                                                                             actual_type, backup_type, is_ext_instance))) {
                                                  YYABORT;
                                                }
                                                s = NULL;
                                                actual_type = EXTENSION_INSTANCE;
                                              }

string_opt_part1: optsep { $$ = s; s = NULL; }

string_opt_part2: @EMPTYDIR@
  |  string

unknown_string: @EMPTYDIR@
  |  sep unknown_string_part1

unknown_string_part1: @EMPTYDIR@
  |  strings optsep
  |  STRING optsep unknown_string_part2

unknown_string_part2: @EMPTYDIR@
  |  unknown_string_part2 '+' optsep STRING optsep

unknown_statement_end: ';'
  |  '{' optsep unknown_statement2_opt '}'

unknown_statement2_opt: @EMPTYDIR@
  |  unknown_statement2_opt unknown_statement2 optsep

/* unknown_statement2 read yang statement or extension; yang statement is parsed later */

unknown_statement2: unknown_statement
  |  unknown_statement2_yang_stmt unknown_string unknown_statement2_end
     {  struct yang_ext_substmt *substmt = ((struct lys_ext_instance *)actual)->parent;
        int32_t length = 0, old_length = 0;
        char *tmp_value;

        if (!substmt) {
          substmt = calloc(1, sizeof *substmt);
          if (!substmt) {
            LOGMEM;
            YYABORT;
          }
          ((struct lys_ext_instance *)actual)->parent = substmt;
        }
        length = strlen($1);
        old_length = (substmt->ext_substmt) ? strlen(substmt->ext_substmt) + 2 : 2;
        tmp_value = realloc(substmt->ext_substmt, old_length + length + 1);
        if (!tmp_value) {
          LOGMEM;
          YYABORT;
        }
        substmt->ext_substmt = tmp_value;
        tmp_value += old_length - 2;
        memcpy(tmp_value, $1, length);
        tmp_value[length] = ' ';
        tmp_value[length + 1] = '\0';
        tmp_value[length + 2] = '\0';
      }
  |  unknown_statement2_module_stmt unknown_string unknown_statement2_end
     {  struct yang_ext_substmt *substmt = ((struct lys_ext_instance *)actual)->parent;
        int32_t length;
        char *tmp_value, **array;
        int i = 0;

        if (!substmt) {
          substmt = calloc(1, sizeof *substmt);
          if (!substmt) {
            LOGMEM;
            YYABORT;
          }
          ((struct lys_ext_instance *)actual)->parent = substmt;
        }
        length = strlen($1);
        if (!substmt->ext_modules) {
          array = malloc(2 * sizeof *substmt->ext_modules);
        } else {
          for (i = 0; substmt->ext_modules[i]; ++i);
          array = realloc(substmt->ext_modules, (i + 2) * sizeof *substmt->ext_modules);
        }
        if (!array) {
          LOGMEM;
          YYABORT;
        }
        substmt->ext_modules = array;
        array[i + 1] = NULL;
        tmp_value = malloc(length + 2);
        if (!tmp_value) {
          LOGMEM;
          YYABORT;
        }
        array[i] = tmp_value;
        memcpy(tmp_value, $1, length);
        tmp_value[length] = '\0';
        tmp_value[length + 1] = '\0';
      }

unknown_statement2_end: ';'
  |  '{' optsep unknown_statement3_opt '}'

unknown_statement2_yang_stmt: yang_stmt { $$ = yyget_text(scanner); }

unknown_statement2_module_stmt: MODULE_KEYWORD { $$ = yyget_text(scanner); }

unknown_statement3_opt: @EMPTYDIR@
  |  unknown_statement3_opt node_identifier unknown_string unknown_statement3_opt_end;

unknown_statement3_opt_end: ';' optsep
  |  '{' optsep unknown_statement3_opt '}' optsep

sep_stmt: WHITESPACE
  | EOL
  ;

optsep: @EMPTYDIR@
  | optsep sep_stmt
  ;

sep: sep_stmt optsep;

whitespace_opt: @EMPTYDIR@
  | WHITESPACE
  ;

string: strings { s = strdup(yyget_text(scanner));
                  if (!s) {
                    LOGMEM;
                    YYABORT;
                  }
                }
        optsep
  |  string_1

strings: STRINGS
  |  REVISION_DATE
  |  identifier
  |  IDENTIFIERPREFIX
  |  ZERO
  |  INTEGER
  |  NON_NEGATIVE_INTEGER

identifier: MODULE_KEYWORD
  |  identifier1
  |  yang_stmt

identifier1: IDENTIFIER
  |  CURRENT_KEYWORD
  |  DEPRECATED_KEYWORD
  |  FALSE_KEYWORD
  |  NOT_SUPPORTED_KEYWORD
  |  OBSOLETE_KEYWORD
  |  SYSTEM_KEYWORD
  |  TRUE_KEYWORD
  |  UNBOUNDED_KEYWORD
  |  USER_KEYWORD


yang_stmt: ANYXML_KEYWORD
  |  ARGUMENT_KEYWORD
  |  AUGMENT_KEYWORD
  |  BASE_KEYWORD
  |  BELONGS_TO_KEYWORD
  |  BIT_KEYWORD
  |  CASE_KEYWORD
  |  CHOICE_KEYWORD
  |  CONFIG_KEYWORD
  |  CONTACT_KEYWORD
  |  CONTAINER_KEYWORD
  |  DEFAULT_KEYWORD
  |  DESCRIPTION_KEYWORD
  |  ENUM_KEYWORD
  |  ERROR_APP_TAG_KEYWORD
  |  ERROR_MESSAGE_KEYWORD
  |  EXTENSION_KEYWORD
  |  DEVIATION_KEYWORD
  |  DEVIATE_KEYWORD
  |  FEATURE_KEYWORD
  |  FRACTION_DIGITS_KEYWORD
  |  GROUPING_KEYWORD
  |  IDENTITY_KEYWORD
  |  IF_FEATURE_KEYWORD
  |  IMPORT_KEYWORD
  |  INCLUDE_KEYWORD
  |  INPUT_KEYWORD
  |  KEY_KEYWORD
  |  LEAF_KEYWORD
  |  LEAF_LIST_KEYWORD
  |  LENGTH_KEYWORD
  |  LIST_KEYWORD
  |  MANDATORY_KEYWORD
  |  MAX_ELEMENTS_KEYWORD
  |  MIN_ELEMENTS_KEYWORD
  |  MUST_KEYWORD
  |  NAMESPACE_KEYWORD
  |  NOTIFICATION_KEYWORD
  |  ORDERED_BY_KEYWORD
  |  ORGANIZATION_KEYWORD
  |  OUTPUT_KEYWORD
  |  PATH_KEYWORD
  |  PATTERN_KEYWORD
  |  POSITION_KEYWORD
  |  PREFIX_KEYWORD
  |  PRESENCE_KEYWORD
  |  RANGE_KEYWORD
  |  REFERENCE_KEYWORD
  |  REFINE_KEYWORD
  |  REQUIRE_INSTANCE_KEYWORD
  |  REVISION_KEYWORD
  |  REVISION_DATE_KEYWORD
  |  RPC_KEYWORD
  |  STATUS_KEYWORD
  |  SUBMODULE_KEYWORD
  |  TYPE_KEYWORD
  |  TYPEDEF_KEYWORD
  |  UNIQUE_KEYWORD
  |  UNITS_KEYWORD
  |  USES_KEYWORD
  |  VALUE_KEYWORD
  |  WHEN_KEYWORD
  |  YANG_VERSION_KEYWORD
  |  YIN_ELEMENT_KEYWORD
  |  ADD_KEYWORD
  |  DELETE_KEYWORD
  |  REPLACE_KEYWORD
  |  ACTION_KEYWORD
  |  MODIFIER_KEYWORD
  |  ANYDATA_KEYWORD

identifiers: identifier { s = strdup(yyget_text(scanner));
                          if (!s) {
                            LOGMEM;
                            YYABORT;
                          }
                        }

identifiers_ref: IDENTIFIERPREFIX { s = strdup(yyget_text(scanner));
                                    if (!s) {
                                      LOGMEM;
                                      YYABORT;
                                    }
                                  }

type_ext_alloc: @EMPTYDIR@ { struct lys_type **type;

                             type = (struct lys_type **)yang_getplace_for_extcomplex_struct(ext_instance, NULL, ext_name,
                                                                                            "type", LY_STMT_TYPE);
                             if (!type) {
                               YYABORT;
                             }
                             /* allocate type structure */
                             (*type) = calloc(1, sizeof **type);
                             if (!*type) {
                               LOGMEM;
                               YYABORT;
                             }

                             /* HACK for unres */
                             (*type)->parent = (struct lys_tpdf *)ext_instance;
                             $$ = actual = *type;
                             is_ext_instance = 0;
                            }

typedef_ext_alloc: @EMPTYDIR@ { struct lys_tpdf **tpdf;

                                tpdf = (struct lys_tpdf **)yang_getplace_for_extcomplex_struct(ext_instance, NULL, ext_name,
                                                                                               "typedef", LY_STMT_TYPEDEF);
                                if (!tpdf) {
                                  YYABORT;
                                }
                                /* allocate typedef structure */
                                (*tpdf) = calloc(1, sizeof **tpdf);
                                if (!*tpdf) {
                                  LOGMEM;
                                  YYABORT;
                                }

                                $$ = actual = *tpdf;
                                is_ext_instance = 0;
                              }

iffeature_ext_alloc: @EMPTYDIR@ { struct lys_iffeature **iffeature;

                                 iffeature = (struct lys_iffeature **)yang_getplace_for_extcomplex_struct(ext_instance, NULL, ext_name,
                                                                                                          "if-feature", LY_STMT_IFFEATURE);
                                 if (!iffeature) {
                                   YYABORT;
                                 }
                                 /* allocate typedef structure */
                                 (*iffeature) = calloc(1, sizeof **iffeature);
                                 if (!*iffeature) {
                                   LOGMEM;
                                   YYABORT;
                                 }
                                 $$ = actual = *iffeature;
                               }

restriction_ext_alloc: @EMPTYDIR@ { struct lys_restr **restr;
                                    LY_STMT stmt;

                                    s = yyget_text(scanner);
                                    if (!strcmp(s, "must")) {
                                      stmt = LY_STMT_MUST;
                                    } else if (!strcmp(s, "pattern")) {
                                      stmt = LY_STMT_PATTERN;
                                    } else if (!strcmp(s, "range")) {
                                      stmt = LY_STMT_RANGE;
                                    } else {
                                      stmt = LY_STMT_LENGTH;
                                    }
                                    restr = (struct lys_restr **)yang_getplace_for_extcomplex_struct(ext_instance, NULL, ext_name, s, stmt);
                                    if (!restr) {
                                      YYABORT;
                                    }
                                    /* allocate structure for must */
                                    (*restr) = calloc(1, sizeof(struct lys_restr));
                                    if (!*restr) {
                                      LOGMEM;
                                      YYABORT;
                                    }
                                    $$ = actual = *restr;
                                    s = NULL;
                                  }

when_ext_alloc: @EMPTYDIR@ { actual = yang_getplace_for_extcomplex_struct(ext_instance, NULL, ext_name, "when", LY_STMT_WHEN);
                             if (!actual) {
                               YYABORT;
                             }
                             $$ = actual;
                           }

revision_ext_alloc: @EMPTYDIR@ { struct lys_revision **rev;
                                 int i;

                                 rev = (struct lys_revision **)yang_getplace_for_extcomplex_struct(ext_instance, &i, ext_name,
                                                                                                   "revision", LY_STMT_REVISION);
                                 if (!rev) {
                                   YYABORT;
                                 }
                                 rev[i] = calloc(1, sizeof **rev);
                                 if (!rev[i]) {
                                   LOGMEM;
                                   YYABORT;
                                 }
                                 actual = rev[i];
                                 $$.revision = rev;
                                 $$.index = i;
                               }

datadef_ext_check: @EMPTYDIR@ { LY_STMT stmt;

                                s = yyget_text(scanner);
                                if (!strcmp(s, "action")) {
                                  stmt = LY_STMT_ACTION;
                                } else if (!strcmp(s, "anydata")) {
                                  stmt = LY_STMT_ANYDATA;
                                } else if (!strcmp(s, "anyxml")) {
                                  stmt = LY_STMT_ANYXML;
                                } else if (!strcmp(s, "case")) {
                                  stmt = LY_STMT_CASE;
                                } else if (!strcmp(s, "choice")) {
                                  stmt = LY_STMT_CHOICE;
                                } else if (!strcmp(s, "container")) {
                                  stmt = LY_STMT_CONTAINER;
                                } else if (!strcmp(s, "grouping")) {
                                  stmt = LY_STMT_GROUPING;
                                } else if (!strcmp(s, "input")) {
                                  stmt = LY_STMT_INPUT;
                                } else if (!strcmp(s, "leaf")) {
                                  stmt = LY_STMT_LEAF;
                                } else if (!strcmp(s, "leaf-list")) {
                                  stmt = LY_STMT_LEAFLIST;
                                } else if (!strcmp(s, "list")) {
                                  stmt = LY_STMT_LIST;
                                } else if (!strcmp(s, "notification")) {
                                  stmt = LY_STMT_NOTIFICATION;
                                } else if (!strcmp(s, "output")) {
                                  stmt = LY_STMT_OUTPUT;
                                } else {
                                  stmt = LY_STMT_USES;
                                }
                                if (yang_extcomplex_node(ext_instance, ext_name, s, *param->node, stmt)) {
                                  YYABORT;
                                }
                                actual = NULL;
                                s = NULL;
                                is_ext_instance = 0;
                              }

not_supported_ext_check: not_supported_ext { LOGERR(LY_SUCCESS, "Extension's substatement \"%s\" not supported.", yyget_text(scanner)); }

not_supported_ext: YANG_VERSION_KEYWORD
  |  YIN_ELEMENT_KEYWORD
  |  BIT_KEYWORD
  |  ENUM_KEYWORD
  |  AUGMENT_KEYWORD
  |  DEVIATION_KEYWORD
  |  DEVIATE_KEYWORD
  |  EXTENSION_KEYWORD
  |  FEATURE_KEYWORD
  |  IDENTITY_KEYWORD
  |  IMPORT_KEYWORD
  |  INCLUDE_KEYWORD
  |  SUBMODULE_EXT_KEYWORD

datadef_ext_stmt: action_stmt
  |  anydata_stmt
  |  anyxml_stmt
  |  case_stmt
  |  choice_stmt
  |  container_stmt
  |  grouping_stmt
  |  input_stmt
  |  leaf_stmt
  |  leaf_list_stmt
  |  list_stmt
  |  notification_stmt
  |  output_stmt
  |  uses_stmt

restriction_ext_stmt: must_stmt
  |  pattern_stmt
  |  range_stmt
  |  length_stmt

ext_substatements: @EMPTYDIR@ { actual_type = EXTENSION_INSTANCE;
                                actual = ext_instance;
                                if (!is_ext_instance) {
                                  LOGVAL(LYE_INSTMT, LY_VLOG_NONE, NULL, yyget_text(scanner));
                                  YYABORT;
                                }
                                $$ = 0;
                              }
  |  ext_substatements belongs_to_stmt stmtsep
  |  ext_substatements prefix_stmt { if (yang_read_extcomplex_str(trg, ext_instance, "prefix", ext_name, s,
                                                                  0, LY_STMT_PREFIX)) {
                                       YYABORT;
                                     }
                                   }
  |  ext_substatements description_stmt { if (yang_read_extcomplex_str(trg, ext_instance, "description", ext_name, s,
                                                                       0, LY_STMT_DESCRIPTION)) {
                                            YYABORT;
                                          }
                                        }
  |  ext_substatements reference_stmt { if (yang_read_extcomplex_str(trg, ext_instance, "reference", ext_name, s,
                                                                     0, LY_STMT_REFERENCE)) {
                                          YYABORT;
                                        }
                                      }
  |  ext_substatements units_stmt { if (yang_read_extcomplex_str(trg, ext_instance, "units", ext_name, s,
                                                                     0, LY_STMT_UNITS)) {
                                      YYABORT;
                                    }
                                  }
  |  ext_substatements base_stmt { if (yang_read_extcomplex_str(trg, ext_instance, "base", ext_name, s,
                                                                0, LY_STMT_BASE)) {
                                     YYABORT;
                                   }
                                 }
  |  ext_substatements contact_stmt { if (yang_read_extcomplex_str(trg, ext_instance, "contact", ext_name, s,
                                                                     0, LY_STMT_CONTACT)) {
                                        YYABORT;
                                      }
                                    }
  |  ext_substatements default_stmt { if (yang_read_extcomplex_str(trg, ext_instance, "default", ext_name, s,
                                                                     0, LY_STMT_DEFAULT)) {
                                        YYABORT;
                                      }
                                    }
  |  ext_substatements error_message_stmt { if (yang_read_extcomplex_str(trg, ext_instance, "error-message", ext_name, s,
                                                                         0, LY_STMT_ERRMSG)) {
                                              YYABORT;
                                            }
                                          }
  |  ext_substatements error_app_tag_stmt { if (yang_read_extcomplex_str(trg, ext_instance, "error-app-tag", ext_name, s,
                                                                         0, LY_STMT_ERRTAG)) {
                                              YYABORT;
                                            }
                                          }
  |  ext_substatements key_stmt { if (yang_read_extcomplex_str(trg, ext_instance, "key", ext_name, s,
                                                               0, LY_STMT_KEY)) {
                                    YYABORT;
                                  }
                                }
  |  ext_substatements namespace_stmt { if (yang_read_extcomplex_str(trg, ext_instance, "namespace", ext_name, s,
                                                                     0, LY_STMT_NAMESPACE)) {
                                          YYABORT;
                                        }
                                      }
  |  ext_substatements organization_stmt { if (yang_read_extcomplex_str(trg, ext_instance, "organization", ext_name, s,
                                                                        0, LY_STMT_ORGANIZATION)) {
                                             YYABORT;
                                           }
                                         }
  |  ext_substatements path_stmt { if (yang_read_extcomplex_str(trg, ext_instance, "path", ext_name, s,
                                                                0, LY_STMT_PATH)) {
                                     YYABORT;
                                   }
                                 }
  |  ext_substatements presence_stmt { if (yang_read_extcomplex_str(trg, ext_instance, "presence", ext_name, s,
                                                                    0, LY_STMT_PRESENCE)) {
                                         YYABORT;
                                       }
                                     }
  |  ext_substatements revision_date_stmt { if (yang_read_extcomplex_str(trg, ext_instance, "revision-date", ext_name, s,
                                                                         0, LY_STMT_REVISIONDATE)) {
                                              YYABORT;
                                            }
                                          }
  |  ext_substatements type_ext_alloc type_stmt stmtsep
     { struct lys_type *type = $2;

       if (yang_fill_type(trg, type, (struct yang_type *)type->der, ext_instance, param->unres)) {
         yang_type_free(trg->ctx, type);
         YYABORT;
       }
       if (unres_schema_add_node(trg, param->unres, type, UNRES_TYPE_DER_EXT, NULL) == -1) {
         yang_type_free(trg->ctx, type);
         YYABORT;
       }
       actual = ext_instance;
       is_ext_instance = 1;
     }
  |  ext_substatements typedef_ext_alloc typedef_stmt stmtsep
     { struct lys_tpdf *tpdf = $2;

       if (yang_fill_type(trg, &tpdf->type, (struct yang_type *)tpdf->type.der, tpdf, param->unres)) {
         yang_type_free(trg->ctx, &tpdf->type);
       }
       if (yang_check_ext_instance(trg, &tpdf->ext, tpdf->ext_size, tpdf, param->unres)) {
         YYABORT;
       }
       if (unres_schema_add_node(trg, param->unres, &tpdf->type, UNRES_TYPE_DER_TPDF, (struct lys_node *)ext_instance) == -1) {
         yang_type_free(trg->ctx, &tpdf->type);
         YYABORT;
       }
       /* check default value*/
       if (unres_schema_add_node(trg, param->unres, &tpdf->type, UNRES_TYPE_DFLT, (struct lys_node *)(&tpdf->dflt)) == -1)  {
         YYABORT;
       }
       actual = ext_instance;
       is_ext_instance = 1;
     }
  |  ext_substatements status_stmt { if (yang_fill_extcomplex_flags(ext_instance, ext_name, "status", LY_STMT_STATUS,
                                                                    $2, LYS_STATUS_MASK)) {
                                       YYABORT;
                                     }
                                   }
  |  ext_substatements config_stmt { if (yang_fill_extcomplex_flags(ext_instance, ext_name, "config", LY_STMT_CONFIG,
                                                                    $2, LYS_CONFIG_MASK)) {
                                       YYABORT;
                                     }
                                   }
  |  ext_substatements mandatory_stmt { if (yang_fill_extcomplex_flags(ext_instance, ext_name, "mandatory", LY_STMT_MANDATORY,
                                                                       $2, LYS_MAND_MASK)) {
                                          YYABORT;
                                        }
                                      }
  |  ext_substatements ordered_by_stmt { if ($1 & LYS_ORDERED_MASK) {
                                            LOGVAL(LYE_TOOMANY, LY_VLOG_NONE, NULL, "ordered by", ext_name);
                                            YYABORT;
                                         }
                                         if ($2 & LYS_USERORDERED) {
                                           if (yang_fill_extcomplex_flags(ext_instance, ext_name, "ordered-by", LY_STMT_ORDEREDBY,
                                                                          $2, LYS_USERORDERED)) {
                                             YYABORT;
                                           }
                                         }
                                         $1 |= $2;
                                         $$ = $1;
                                       }
  |  ext_substatements require_instance_stmt { if (yang_fill_extcomplex_uint8(ext_instance, ext_name, "require-instance",
                                                                              LY_STMT_REQINSTANCE, $2)) {
                                                 YYABORT;
                                               }
                                             }
  |  ext_substatements modifier_stmt { if (yang_fill_extcomplex_uint8(ext_instance, ext_name, "modifier", LY_STMT_MODIFIER, 0)) {
                                         YYABORT;
                                       }
                                     }
  |  ext_substatements fraction_digits_stmt
     { /* range check */
       if ($2 < 1 || $2 > 18) {
         LOGVAL(LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid value \"%d\" of \"%s\".", $2, "fraction-digits");
         YYABORT;
       }
       if (yang_fill_extcomplex_uint8(ext_instance, ext_name, "fraction-digits", LY_STMT_DIGITS, $2)) {
         YYABORT;
       }
     }
  |  ext_substatements min_elements_stmt { uint32_t **val;

                                           val = (uint32_t **)yang_getplace_for_extcomplex_struct(ext_instance, NULL, ext_name,
                                                                                                  "min-elements", LY_STMT_MIN);
                                           if (!val) {
                                             YYABORT;
                                           }
                                           /* store the value */
                                           *val = malloc(sizeof(uint32_t));
                                           if (!*val) {
                                             LOGMEM;
                                             YYABORT;
                                           }
                                           **val = $2;
                                         }
  |  ext_substatements max_elements_stmt { uint32_t **val;

                                           val = (uint32_t **)yang_getplace_for_extcomplex_struct(ext_instance, NULL, ext_name,
                                                                                                  "max-elements", LY_STMT_MAX);
                                           if (!val) {
                                             YYABORT;
                                           }
                                           /* store the value */
                                           *val = malloc(sizeof(uint32_t));
                                           if (!*val) {
                                             LOGMEM;
                                             YYABORT;
                                           }
                                           **val = $2;
                                         }
  |  ext_substatements position_stmt { uint32_t **val;

                                       val = (uint32_t **)yang_getplace_for_extcomplex_struct(ext_instance, NULL, ext_name,
                                                                                              "position", LY_STMT_POSITION);
                                       if (!val) {
                                         YYABORT;
                                       }
                                       /* store the value */
                                       *val = malloc(sizeof(uint32_t));
                                       if (!*val) {
                                         LOGMEM;
                                         YYABORT;
                                       }
                                       **val = $2;
                                     }
  |  ext_substatements value_stmt { int32_t **val;

                                    val = (int32_t **)yang_getplace_for_extcomplex_struct(ext_instance, NULL, ext_name,
                                                                                          "value", LY_STMT_VALUE);
                                    if (!val) {
                                      YYABORT;
                                    }
                                    /* store the value */
                                    *val = malloc(sizeof(int32_t));
                                    if (!*val) {
                                      LOGMEM;
                                      YYABORT;
                                    }
                                    **val = $2;
                                  }
  |  ext_substatements unique_stmt { struct lys_unique **unique;
                                     int rc;

                                     unique = (struct lys_unique **)yang_getplace_for_extcomplex_struct(ext_instance, NULL, ext_name,
                                                                                                        "unique", LY_STMT_UNIQUE);
                                     if (!unique) {
                                       YYABORT;
                                     }
                                     *unique = calloc(1, sizeof(struct lys_unique));
                                     if (!*unique) {
                                       LOGMEM;
                                       YYABORT;
                                     }
                                     rc = yang_fill_unique(trg, (struct lys_node_list *)ext_instance, *unique, s, param->unres);
                                     free(s);
                                     s = NULL;
                                     if (rc) {
                                       YYABORT;
                                     }
                                   }
  |  ext_substatements iffeature_ext_alloc if_feature_stmt stmtsep
     { struct lys_iffeature *iffeature;

       iffeature = $2;
       s = (char *)iffeature->features;
       iffeature->features = NULL;
       if (yang_fill_iffeature(trg, iffeature, ext_instance, s, param->unres, 0)) {
         YYABORT;
       }
       if (yang_check_ext_instance(trg, &iffeature->ext, iffeature->ext_size, iffeature, param->unres)) {
         YYABORT;
       }
       s = NULL;
       actual = ext_instance;
     }
  |  ext_substatements argument_stmt stmtsep
  |  ext_substatements restriction_ext_alloc restriction_ext_stmt stmtsep
     { if (yang_check_ext_instance(trg, &((struct lys_restr *)$2)->ext, ((struct lys_restr *)$2)->ext_size, $2, param->unres)) {
         YYABORT;
       }
       actual = ext_instance;
     }
  |  ext_substatements when_ext_alloc when_stmt stmtsep
     { if (yang_check_ext_instance(trg, &(*(struct lys_when **)$2)->ext, (*(struct lys_when **)$2)->ext_size,
                                   *(struct lys_when **)$2, param->unres)) {
         YYABORT;
       }
       actual = ext_instance;
     }
  |  ext_substatements revision_ext_alloc revision_stmt stmtsep
     { int i;

       for (i = 0; i < $2.index; ++i) {
         if (!strcmp($2.revision[i]->date, $2.revision[$2.index]->date)) {
           LOGWRN("Module's revisions are not unique (%s).", $2.revision[i]->date);
           break;
         }
       }
       if (yang_check_ext_instance(trg, &$2.revision[$2.index]->ext, $2.revision[$2.index]->ext_size,
                                   &$2.revision[$2.index], param->unres)) {
         YYABORT;
       }
       actual = ext_instance;
     }
  |  ext_substatements datadef_ext_check datadef_ext_stmt stmtsep { actual = ext_instance;
                                                                    is_ext_instance = 1;
                                                                  }
  |  ext_substatements not_supported_ext_check unknown_string unknown_statement3_opt_end

%%

void yyerror(YYLTYPE *yylloc, void *scanner, struct yang_parameter *param, ...){

  free(*param->value);
  if (yylloc->first_line != -1) {
    if (*param->data_node && (*param->data_node) == (*param->actual_node)) {
      LOGVAL(LYE_INSTMT, LY_VLOG_LYS, *param->data_node, yyget_text(scanner));
    } else {
      LOGVAL(LYE_INSTMT, LY_VLOG_NONE, NULL, yyget_text(scanner));
    }
  }
}
