+%{
+#include <stdlib.h>
+#include <string.h>
+#include "oconfig.h"
+
+static oconfig_item_t *ci_root;
+static oconfig_item_t *ci_current;
+
+static oconfig_item_t *statement_list;
+static int statement_list_num;
+
+static oconfig_value_t *argument_list;
+static int argument_list_num;
+
+struct statement_list_s
+{
+ oconfig_item_t *statement;
+ int statement_num;
+};
+typedef struct statement_list_s statement_list_t;
+
+struct argument_list_s
+{
+ oconfig_value_t *argument;
+ int argument_num;
+};
+typedef struct argument_list_s argument_list_t;
+
+static void dump_ci (oconfig_item_t *ci, int shift);
+%}
+
+%start entire_file
+
+%union {
+ double number;
+ int boolean;
+ char *string;
+ oconfig_value_t cv;
+ oconfig_value_t *cvp;
+ oconfig_item_t ci;
+ argument_list_t al;
+ statement_list_t sl;
+}
+
+%token <number> NUMBER
+%token <boolean> TRUE FALSE
+%token <string> QUOTED_STRING UNQUOTED_STRING
+%token SLASH OPENBRAC CLOSEBRAC EOL
+
+%type <string> string
+%type <string> identifier
+%type <string> block_end
+%type <cv> argument
+%type <al> argument_list
+%type <ci> block_begin
+%type <ci> block
+%type <ci> option
+%type <ci> statement
+%type <sl> statement_list
+
+%%
+string:
+ QUOTED_STRING {$$ = $1;}
+ | UNQUOTED_STRING {$$ = $1;}
+ ;
+
+argument:
+ NUMBER {$$.value.number = $1; $$.type = OCONFIG_TYPE_NUMBER;}
+ | TRUE {$$.value.boolean = 1; $$.type = OCONFIG_TYPE_BOOLEAN;}
+ | FALSE {$$.value.boolean = 0; $$.type = OCONFIG_TYPE_BOOLEAN;}
+ | string {$$.value.string = $1; $$.type = OCONFIG_TYPE_STRING;}
+ ;
+
+argument_list:
+ argument_list argument
+ {
+ $$ = $1;
+ $$.argument_num++;
+ $$.argument = realloc ($$.argument, $$.argument_num * sizeof (argument_list_t));
+ $$.argument[$$.argument_num-1] = $2;
+ }
+ | argument
+ {
+ $$.argument = malloc (sizeof (argument_list_t));
+ $$.argument[0] = $1;
+ $$.argument_num = 1;
+ }
+ ;
+
+identifier:
+ UNQUOTED_STRING {$$ = $1;}
+ ;
+
+option:
+ identifier argument_list EOL
+ {
+ memset (&$$, '\0', sizeof ($$));
+ $$.key = $1;
+ $$.values = $2.argument;
+ $$.values_num = $2.argument_num;
+ printf ("Option `%s' has %i arguments\n", $$.key, $$.values_num);
+ }
+ ;
+
+block_begin:
+ OPENBRAC identifier argument_list CLOSEBRAC EOL
+ {
+ memset (&$$, '\0', sizeof ($$));
+ $$.key = $2;
+ $$.values = $3.argument;
+ $$.values_num = $3.argument_num;
+ printf ("Begin block `%s'\n", $2);
+ }
+ ;
+
+block_end:
+ OPENBRAC SLASH identifier CLOSEBRAC EOL {$$ = $3; printf ("End block `%s'\n", $3);}
+ ;
+
+block:
+ block_begin statement_list block_end
+ {
+ if (strcmp ($1.key, $3) != 0)
+ yyerror ("Block %s not closed..\n", $1);
+ $$ = $1;
+ $$.children = $2.statement;
+ }
+ ;
+
+statement:
+ option {$$ = $1;}
+ | block {$$ = $1;}
+ | EOL {/* ignore */}
+ ;
+
+statement_list:
+ statement_list statement
+ {
+ $$ = $1;
+ $$.statement_num++;
+ $$.statement = realloc ($$.statement, $$.statement_num * sizeof (statement_list_t));
+ $$.statement[$$.statement_num-1] = $2;
+ printf ("statement_list: length = %i\n", $$.statement_num);
+ }
+ | statement
+ {
+ $$.statement = malloc (sizeof (statement_list_t));
+ $$.statement[0] = $1;
+ $$.statement_num = 1;
+ }
+ ;
+
+entire_file:
+ statement_list
+ {
+ int i;
+ for (i = 0; i < $1.statement_num; i++)
+ dump_ci ($1.statement + i, 0);
+ }
+ ;
+
+%%
+#include "lex.yy.c"
+
+/*
+void yyerror (char *s)
+{
+ fprintf (stderr, "%s\n", s);
+}
+
+int yylex (void)
+{
+ return (getc (stdin));
+}
+*/
+
+int main (int argc, char **argv)
+{
+ yyparse ();
+ return (0);
+}
+
+static void dump_ci (oconfig_item_t *ci, int shift)
+{
+ int i;
+
+ if (shift > 0)
+ printf ("%*s", "", shift);
+
+ printf ("%s", ci->key);
+ for (i = 0; i < ci->values_num; i++)
+ {
+ oconfig_value_t cv = ci->values[i];
+
+ if (cv.type == OCONFIG_TYPE_STRING)
+ printf (" `%s'", cv.value.string);
+ else if (cv.type == OCONFIG_TYPE_NUMBER)
+ printf (" %lf", cv.value.number);
+ else if (cv.type == OCONFIG_TYPE_BOOLEAN)
+ printf (" %s", cv.value.boolean ? "true" : "false");
+ else
+ printf ("<unknown type %i>", cv.type);
+ }
+ printf ("\n");
+
+ for (i = 0; i < ci->children_num; i++)
+ dump_ci (ci->children + i, shift + 1);
+}