The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/**
 * oconfig - src/parser.y
 * Copyright (C) 2007,2008  Florian octo Forster <octo at verplant.org>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; only version 2 of the License is applicable.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */

%{
sub YYERROR {
	join " ", @_;
}
%}

%start entire_file

%union {
	double  number;
	int     boolean;
	char   *string;
	oconfig_value_t  cv;
	oconfig_item_t   ci;
	argument_list_t  al;
	statement_list_t sl;
}

%token <number> NUMBER
%token <boolean> BTRUE BFALSE
%token <string> QUOTED_STRING UNQUOTED_STRING
%token SLASH OPENBRAC CLOSEBRAC EOL

%type <string> string
%type <string> identifier
/* arguments */
%type <cv> argument
%type <al> argument_list
/* blocks */
%type <ci> block_begin
%type <ci> block
%type <string> block_end
/* statements */
%type <ci> option
%type <ci> statement
%type <sl> statement_list
%type <ci> entire_file

/* pass an verbose, specific error message to yyerror() */
/* this is bison only jargon %error-verbose */

%%
string:
	QUOTED_STRING		{ unquote ($_[1]);}
	| UNQUOTED_STRING	{$_[1];}
	;

argument:
	NUMBER			{$_[1]}
	| BTRUE			{1}
	| BFALSE		{0}
	| string		{$_[1]}
	;

argument_list:
	argument_list argument
	{
	 my $argument_list = $_[1];
	 push @{ $argument_list->{values} }, $_[2];
	 return $argument_list;
	}
	| argument
	{
	 { values => [ $_[1] ] };
	}
	;

identifier:
	UNQUOTED_STRING			{$_[1]}
	;

option:
	identifier argument_list EOL
	{
		{
			children => [],
			key => $_[1],
			%{$_[2]},
		};
	}
	;

block_begin:
	OPENBRAC identifier CLOSEBRAC EOL
	{
	 {key => $_[2], values => []};
	}
	|
	OPENBRAC identifier argument_list CLOSEBRAC EOL
	{
	 {key => $_[2], %{$_[3]}};
	}
	;

block_end:
	OPENBRAC SLASH identifier CLOSEBRAC EOL
	{
	 $_[3];
	}
	;

block:
	block_begin statement_list block_end
	{
	 if ($_[1]->{key} ne $_[3])
	 {
		printf ("block_begin = %s; block_end = %s;\n", $_[1]->{key}, $_[3]);
	 	YYERROR ("Block not closed..\n");
		die;
	 }
	 return {
		%{$_[1]},
		children => $_[2],
	 };
	}
	;

statement:
	option		{ $_[1] }
	| block		{ $_[1] }
	| EOL		{ return }
	;

statement_list:
	statement_list statement
	{
	 my $statement_list = $_[1];
	 push @{ $statement_list }, $_[2] if defined $_[2];
	 return $statement_list;
	}
	| statement
	{
		if (defined $_[1]) {
			return [ $_[1] ];
		} else {
			return;
		}
	}
	;

entire_file:
	statement_list
	{
		$_[1]->[0];
	}
	;

%%
sub unquote ($) {
	(my $string = shift) =~ s/(?<!\\)"//g;
	return $string;
}