%{
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "error.h"
#include "malloc.h"
#include "domnode-xml.h"

/* Generated by bison(1) */
#include "domnode-xml-parser.h"

/* Generated by flex(1) */
#define YY_HEADER_NO_UNDEFS 1
#include "domnode-xml-scanner.h"

#define YYPARSE_PARAM __a_maker
#define a_maker ((struct __sd_domnode_xml_maker*) YYPARSE_PARAM)
#define YYLEX_PARAM (a_maker->scanner)

#ifdef strdup
#	undef strdup
#endif
#define strdup sd_strdup
#ifdef malloc
#	undef malloc
#endif
#define malloc sd_malloc
#ifdef calloc
#	undef calloc
#endif
#define calloc sd_calloc
#ifdef realloc
#	undef realloc
#endif
#define realloc sd_realloc
#ifdef yyerror
#	undef yyerror
#endif
#define yyerror sd_error

static void domnode_attribute(struct __sd_domnode_xml_maker*, const char*,
			       const char*);
%}

%name-prefix="__sd_domnode_xml_"
%pure_parser
%debug
%defines
%output="domnode-xml-parser.c"

%union { char *s; }

%token ENDDEF EQ SLASH CLOSE END
%token <s> NAME VALUE DATA COMMENT START
%type <s> name

%%

document
: misc element misc
;

misc
: misc comment
| /* empty */
;

comment
: COMMENT
{
    sd_domnode_t* node = sd_stack_peek(a_maker->elements);
    
    if (node) {
	sd_debug("COMMENT: add comment '%s' to node '%s'\n", $1, node->name);
	sd_list_append(node->children, __sd_domnode_new("#comment", $1, 0));
	/* $1 was obtain with strdup() */
	free($1);
    }
}
;

element
: START
{
    sd_domnode_t* parent = sd_stack_peek(a_maker->elements);
    sd_domnode_t* node = __sd_domnode_new($1, 0, 1);
    
#ifdef __SD_DEBUG__
    {
	static void* __debug = 0;
	if (! __debug) {
	    __debug = getenv("SD_DEBUG");
	    if (__debug)
		yydebug = 1;
	    else
		__debug = (void*) 0x1;
	}
    }
#endif
    
    if (parent) {
	sd_debug("START: add child '%s' to node '%s'\n", $1, parent->name);
	sd_list_append(parent->children, node);
    } else {
	sd_debug("START: add root'%s'\n", $1);
	a_maker->root = node;
    }
    
    sd_stack_push(a_maker->elements, node);
    free($1);
}
attribute_seq_opt
empty_or_content
;

empty_or_content
: SLASH CLOSE
{
    sd_domnode_t* node = sd_stack_peek(a_maker->elements);
    assert(node != 0);
    
    sd_debug("END: simple node '%s'\n", node->name);
    sd_stack_pop(a_maker->elements);
}
| CLOSE content END name CLOSE
{
    sd_domnode_t* node = sd_stack_peek(a_maker->elements);
    assert(node != 0);
    
    sd_debug("END: node '%s'\n", $4);
    
    if (strcmp(node->name, $4)) {
	yyerror("got </%s> instead of </%s>\n", $4, node->name);
	YYERROR;
    }
    
    /* $4 was obtain with strdup() */
    free($4);
    
    sd_stack_pop(a_maker->elements);
}
;

content
: content DATA
{
    sd_domnode_t* node = sd_stack_peek(a_maker->elements);
    assert(node != 0);
    
    sd_debug("CONTENT: add cvalue '%s' to node '%s'\n", $2, node->name);
    
    /* $2 was obtain with strdup() */
    node->value = $2;
}
| content element
| content comment
| /*empty*/
;

name
: NAME
{
    $$ = $1;
}
;

attribute_seq_opt
: attribute_seq_opt attribute
| /*empty*/
;

attribute
: NAME
{
    domnode_attribute(a_maker, $1, "");
    /* $1 was obtain with strdup() */
    free($1);
}
| NAME EQ VALUE
{
    domnode_attribute(a_maker, $1, $3);
    /* $1 was obtain with strdup() */
    free($1);
    /* $3 was obtain with strdup() */
    free($3);
}
;

%%
#undef a_maker

/******************************************************************************/
static void domnode_attribute(struct __sd_domnode_xml_maker* a_maker,
			       const char* a_name, const char* a_value)
{
    sd_domnode_t* node = sd_stack_peek(a_maker->elements);
    assert(node != 0);
    
    sd_debug("ATTR: add atrribute '%s'='%s' to node '%s'\n", a_name, a_value,
	     node->name);
    
    sd_list_append(node->attrs, __sd_domnode_new(a_name, a_value, 0));
}
