%{
/*
**  OSSP cfg - Configuration Parsing
**  Copyright (c) 2002-2003 Ralf S. Engelschall <rse@engelschall.com>
**  Copyright (c) 2002-2003 The OSSP Project (http://www.ossp.org/)
**  Copyright (c) 2002-2003 Cable & Wireless Deutschland (http://www.cw.com/de/)
**
**  This file is part of OSSP cfg, a configuration parsing
**  library which can be found at http://www.ossp.org/pkg/lib/cfg/.
**
**  Permission to use, copy, modify, and distribute this software for
**  any purpose with or without fee is hereby granted, provided that
**  the above copyright notice and this permission notice appear in all
**  copies.
**
**  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
**  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
**  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
**  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
**  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
**  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
**  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
**  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
**  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
**  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
**  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
**  SUCH DAMAGE.
**
**  cfg_syn_parse.y: context free grammar specification for GNU Bison
**
**  ATTENTION: This requires GNU Bison 1.875 or newer!
*/

#include <stdio.h>
#include <stdlib.h>

#include "cfg.h"
#include "cfg_global.h"
#include "cfg_syn.h"

/* make sure yyparse() accepts a context pointer and
   passes through its inlined scanner context to yylex() */
#define CTX           ((cfg_syn_ctx_t *)ctx)
#define YYPARSE_PARAM ctx
#define YYLEX_PARAM   CTX->yyscan

/* provide an explicit prototype for the yylex() function but use
   "void *" instead of correct type because at this point in the
   generation process we have the types still not available */
extern int yylex(/*YYSTYPE*/ void *lvalp,
                 /*YYLTYPE*/ void *llocp, cfg_syn_ctx_t *ctx);

/* generate verbose error messages and remember them inside the context */
#undef  yyerror
#define yyerror(msg) \
    cfg_syn_error(CTX, CFG_ERR_SYN, &yylloc, msg)

/* scanner state transition */
extern void cfg_syn_scan_push(cfg_syn_ctx_t *, const char *state);
extern void cfg_syn_scan_pop(cfg_syn_ctx_t *);
%}

/* parser options */
%error-verbose
%pure_parser
%locations
%defines

/* the YYSTYPE: parser token value */
%union {
    char         *cpString;
    cfg_node_t   *npNode;
}

/* assign YYSTYPE elements to parser rules */
%type  <npNode>   sequence
%type  <npNode>   directives
%type  <npNode>   directive
%type  <npNode>   tokens
%type  <npNode>   token
%type  <npNode>   string

/* list of scanner tokens */
%token            T_SEP
%token            T_OPEN
%token            T_CLOSE
%token <cpString> T_STRING

/* operator association */
%right T_SEP
%right T_OPEN
%right T_CLOSE

/* grammar start rule
   (technically redundant but explicit to be sure) */
%start configuration

%%

configuration
    : sequence {
        CTX->node = $1;
      }
    ;

sequence
    : directives {
        cfg_node_t *n;
        cfg_node_create(CTX->cfg, &n);
        cfg_node_set(CTX->cfg, n, CFG_NODE_ATTR_TYPE, CFG_NODE_TYPE_SEQ);
        cfg_node_set(CTX->cfg, n, CFG_NODE_ATTR_CHILD1, $1);
        $$ = n;
      }
    ;

directives
    : /* empty */ {
        $$ = NULL;
      }
    | directive {
        $$ = $1;
      }
    | directive T_SEP directives {
        cfg_node_set(CTX->cfg, $1, CFG_NODE_ATTR_RBROTH, $3);
        $$ = $1;
      }
    ;

directive
    : tokens {
        cfg_node_t *n;
        cfg_node_create(CTX->cfg, &n);
        cfg_node_set(CTX->cfg, n, CFG_NODE_ATTR_TYPE, CFG_NODE_TYPE_DIR);
        cfg_node_set(CTX->cfg, n, CFG_NODE_ATTR_CHILD1, $1);
        $$ = n;
      }
    ;

tokens
    : token {
        $$ = $1;
      }
    | token tokens {
        cfg_node_set(CTX->cfg, $1, CFG_NODE_ATTR_RBROTH, $2);
        $$ = $1;
      }
    ;

token
    : T_OPEN sequence T_CLOSE {
        $$ = $2;
      }
    | string {
        $$ = $1;
      }
    ;

string
    : T_STRING {
        cfg_node_t *n;
        cfg_node_create(CTX->cfg, &n);
        cfg_node_set(CTX->cfg, n, CFG_NODE_ATTR_TYPE, CFG_NODE_TYPE_ARG);
        cfg_node_set(CTX->cfg, n, CFG_NODE_ATTR_TOKEN, $1);
        $$ = n;
      }
    ;

%%

