/* $Id: grammar.lemon,v 1.10 2004/06/03 07:36:24 guy Exp $ */

%include {
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "dfilter-int.h"
#include "syntax-tree.h"
#include "sttype-range.h"
#include "sttype-test.h"
#include "drange.h"

/* End of C code */
}

/* Parser Information */
%name		Dfilter
%token_prefix	TOKEN_
%extra_argument	{dfwork_t *dfw}

/* Terminal and Non-Terminal types and destructors */
%token_type			{stnode_t*}
%token_destructor		{stnode_free($$);}

%type		sentence	{stnode_t*}
%destructor	sentence	{stnode_free($$);}

%type		expr		{stnode_t*}
%destructor	expr		{stnode_free($$);}

%type		entity		{stnode_t*}
%destructor	entity		{stnode_free($$);}

%type		relation_test	{stnode_t*}
%destructor	relation_test	{stnode_free($$);}

%type		logical_test	{stnode_t*}
%destructor	logical_test	{stnode_free($$);}

%type		rel_op2		{test_op_t}

%type		range		{stnode_t*}
%destructor	range		{stnode_free($$);}

%type		drnode		{drange_node*}
%destructor	drnode		{drange_node_free($$);}

%type		drnode_list	{GSList*}
%destructor	drnode_list	{drange_node_free_list($$);}

/* This is called as soon as a syntax error happens. After that, 
any "error" symbols are shifted, if possible. */
%syntax_error {

	header_field_info	*hfinfo;

	if (!TOKEN) {
		dfilter_fail("Unexpected end of filter string.");
		return;
	}

	switch(stnode_type_id(TOKEN)) {
	        case STTYPE_UNINITIALIZED:
			dfilter_fail("Syntax error.");
			break;
		case STTYPE_TEST:
			dfilter_fail("Syntax error, TEST.");
			break;
		case STTYPE_STRING:
			dfilter_fail("The string \"%s\" was unexpected in this context.",
				stnode_data(TOKEN));
			break;
		case STTYPE_UNPARSED:
			dfilter_fail("\"%s\" was unexpected in this context.",
				stnode_data(TOKEN));
			break;
		case STTYPE_INTEGER:
			dfilter_fail("The integer %d was unexpected in this context.",
				stnode_value(TOKEN));
			break;
		case STTYPE_FIELD:
			hfinfo = stnode_data(TOKEN);
			dfilter_fail("Syntax error near \"%s\".", hfinfo->abbrev);
			break;

		/* These aren't handed to use as terminal tokens from
		   the scanner, so was can assert that we'll never
		   see them here. */
		case STTYPE_NUM_TYPES:
		case STTYPE_RANGE:
		case STTYPE_FVALUE:
			g_assert_not_reached();
			break;
	}
}

/* When a parse fails, mark an error. This occurs after
the above syntax_error code and after the parser fails to
use error recovery, shifting an "error" symbol and successfully
shifting 3 more symbols. */
%parse_failure {
	dfw->syntax_error = TRUE;
}

/* ----------------- The grammar -------------- */

/* Associativity */
%left TEST_AND.
%left TEST_OR.
%nonassoc TEST_EQ TEST_NE TEST_LT TEST_LE TEST_GT TEST_GE TEST_CONTAINS TEST_MATCHES TEST_BITWISE_AND.
%right TEST_NOT.

/* Top-level targets */
sentence ::= expr(X).  		{ dfw->st_root = X; }
sentence ::= .  		{ dfw->st_root = NULL; }

expr(X) ::= relation_test(R).	{ X = R; }
expr(X) ::= logical_test(L).	{ X = L; }

expr(X) ::= LPAREN expr(Y) RPAREN.
{
	X = Y;
}


/* Logical tests */
logical_test(T) ::= expr(E) TEST_AND expr(F).
{
	T = stnode_new(STTYPE_TEST, NULL);
	sttype_test_set2(T, TEST_OP_AND, E, F);
}

logical_test(T) ::= expr(E) TEST_OR expr(F).
{
	T = stnode_new(STTYPE_TEST, NULL);
	sttype_test_set2(T, TEST_OP_OR, E, F);
}

logical_test(T) ::= TEST_NOT expr(E).
{
	T = stnode_new(STTYPE_TEST, NULL);
	sttype_test_set1(T, TEST_OP_NOT, E);
}

logical_test(T) ::= entity(E).
{
	T = stnode_new(STTYPE_TEST, NULL);
	sttype_test_set1(T, TEST_OP_EXISTS, E);
}



/* Entities, or things that can be compared/tested/checked */
entity(E) ::= FIELD(F).		{ E = F; }
entity(E) ::= STRING(S).	{ E = S; }
entity(E) ::= UNPARSED(U).	{ E = U; }
entity(E) ::= range(R).		{ E = R; }


/* Ranges */
range(R) ::= FIELD(F) LBRACKET drnode_list(L) RBRACKET.
{
	R = stnode_new(STTYPE_RANGE, NULL);
	sttype_range_set(R, F, L);

	/* Delete the list, but not the drange_nodes that
	 * the list contains. */
	g_slist_free(L);
}

drnode_list(L) ::= drnode(D).
{
	L = g_slist_append(NULL, D);
}

drnode_list(L) ::= drnode_list(P) COMMA drnode(D).
{
	L = g_slist_append(P, D);
}

/* x:y is offset:length */
drnode(D) ::= INTEGER(X) COLON INTEGER(Y).
{
	D = drange_node_new();
	drange_node_set_start_offset(D, stnode_value(X));
	drange_node_set_length(D, stnode_value(Y));
	
	stnode_free(X);
	stnode_free(Y);
}

/* x-y == offset:offset */
drnode(D) ::= INTEGER(X) HYPHEN INTEGER(Y).
{
	D = drange_node_new();
	drange_node_set_start_offset(D, stnode_value(X));
	drange_node_set_end_offset(D, stnode_value(Y));
	
	stnode_free(X);
	stnode_free(Y);
}


/* :y == from start to offset */
drnode(D) ::= COLON INTEGER(Y).
{
	D = drange_node_new();
	drange_node_set_start_offset(D, 0);
	drange_node_set_length(D, stnode_value(Y));

	stnode_free(Y);
}

/* x: from offset to end */
drnode(D) ::= INTEGER(X) COLON.
{
	D = drange_node_new();
	drange_node_set_start_offset(D, stnode_value(X));
	drange_node_set_to_the_end(D);

	stnode_free(X);
}

/* x == x:1 */
drnode(D) ::= INTEGER(X).
{
	D = drange_node_new();
	drange_node_set_start_offset(D, stnode_value(X));
	drange_node_set_length(D, 1);

	stnode_free(X);
}



/* Relational tests */
relation_test(T) ::= entity(E) rel_op2(O) entity(F).
{
	T = stnode_new(STTYPE_TEST, NULL);
	sttype_test_set2(T, O, E, F);
}

rel_op2(O) ::= TEST_EQ.  { O = TEST_OP_EQ; }
rel_op2(O) ::= TEST_NE.  { O = TEST_OP_NE; }
rel_op2(O) ::= TEST_GT.  { O = TEST_OP_GT; }
rel_op2(O) ::= TEST_GE.  { O = TEST_OP_GE; }
rel_op2(O) ::= TEST_LT.  { O = TEST_OP_LT; }
rel_op2(O) ::= TEST_LE.  { O = TEST_OP_LE; }
rel_op2(O) ::= TEST_BITWISE_AND.  { O = TEST_OP_BITWISE_AND; }
rel_op2(O) ::= TEST_CONTAINS.  { O = TEST_OP_CONTAINS; }
rel_op2(O) ::= TEST_MATCHES.  { O = TEST_OP_MATCHES; }



