%{
#include <stdio.h>
#include <string.h>
#include "util.h"
#include "template.h"
#include "post_process.h"

#define FLEX_SCANNER

extern char *name_buf;     /* this is strdup'ed in the lexer */
extern char *qstr_buf;     /* this is strdup'ed in the lexer */
extern char *word_buf;     /* this is strdup'ed in the lexer */

static Word *mk_wordlist ();
static Word *add_word ();
static Inst *mk_inst ();
static Inst *add_inst ();
static Cond *mk_cond ();
static Cond *add_cond ();
static Rule *mk_rule ();
static Rule *add_rule ();

#ifndef FLEX_SCANNER
extern int yylineno;
#endif

%}


%token END_ LTAB_
%token AND_ OR_ COMMA_
%token BANG_ PIPE_ TWIDDLE_ EQUALS_ ASSIGN_ NOTEQUALS_ NOTTWIDDLE_
%token DELETE_
%token NAME_ QSTR_ DQSTR_ PATTERN_ WORD_
%token COMMENT_

%union{
	Word	*WORD;
	Cond	*COND;
	Inst	*INST;
	Rule	*RULE;
	int	INT;
}

%type <WORD>	wordlist attrs qstr dqstr pattern word attr condattr condstr
%type <RULE>	rule rules config;
%type <COND>	condition conditions;
%type <INST>	instruction instructions;
%type <INT>	AND_ OR_ EQUALS_ NOTEQUALS_ TWIDDLE_ NOTTWIDDLE_ PIPE_ BANG_ ASSIGN_ condop

%%
config		: rules				{ PPRules = $1; }
		;

rules		: rule				{ $$ = $1; }
		| rule rules			{ $$ = add_rule ($1, $2); }
		;

rule		: conditions END_ instructions 	{ $$ = mk_rule ($1, $3); }
		;

conditions	: condition			{ $$ = $1; }
		| conditions AND_ condition	{ $$ = add_cond ($1, $3, AND); }
		| conditions OR_  condition	{ $$ = add_cond ($1, $3, OR); }
		;

condition	: condattr condop condstr	{ $$ = mk_cond ($1, $2, $3); }

condattr	: word				{ $$ = $1; }
		;

condop		: EQUALS_			{ $$ = EQUALS; }
		| NOTEQUALS_			{ $$ = NOTEQ; }
		| TWIDDLE_			{ $$ = REGEX; }
		| NOTTWIDDLE_			{ $$ = NOTRE; }
		;

condstr		: qstr				{ $$ = $1; }
		| dqstr				{ $$ = $1; }
		| pattern			{ $$ = $1; }
		;

instructions	: instruction			{ $$ = $1; }
		| instructions instruction	{ $$ = add_inst ($1, $2); }
		;

instruction	: LTAB_ attr PIPE_   wordlist END_	{ $$ = mk_inst ($2, PIPE,   $4); }
		| LTAB_ attr ASSIGN_ dqstr END_		{ $$ = mk_inst ($2, ASSIGN, $4); }
		| LTAB_ attrs BANG_  wordlist END_	{ $$ = mk_inst ($2, BANG,   $4); }
		| LTAB_ DELETE_ END_			{ $$ = mk_inst (NULL, DELETE, NULL); }
		;

attrs		: attr				{ $$ = $1; }
		| attrs COMMA_ attr		{ $$ = add_word ($1, $3); }
		;

attr		: word				{ $$ = $1; }
		;

wordlist	: word 				{ $$ = $1; }
		| wordlist word			{ $$ = add_word ($1, $2); }
		;

qstr		: QSTR_				{ $$ = mk_wordlist (qstr_buf, '\''); }
		;

dqstr		: DQSTR_			{ $$ = mk_wordlist (qstr_buf, '"'); }
		;

word		: WORD_				{ $$ = mk_wordlist (word_buf, 0); }
		;

pattern		: PATTERN_			{ $$ = mk_wordlist (qstr_buf, '/'); }
		;


%%

static Word *mk_wordlist (word, delim)
	char	*word;
	int	delim;
{
	Word	*t;
	char	*s;

	/* remove delimeters */
	if (*word == (char) delim)
		word++;
	s	= word+strlen(word)-1;
	if (*s == (char) delim)
		*s = 0;
	
	t	= (Word *) xmalloc (sizeof (Word));
	t->word	= (char *) xstrdup (word);
	t->next	= 0;
	return t;
}

static Word *add_word (wordlist, word)
	Word	*wordlist;
	Word	*word;
{
	Word	*t;
	for (t=wordlist; t->next; t=t->next);
	t->next = word;
	return wordlist;
}

static Inst *mk_inst (attrs, op, args)
	Word	*attrs;
	int	op;
	Word	*args;
{
	Inst		*t;
	t		= (Inst *) xmalloc (sizeof (Inst));
	t->attrs	= attrs;
	t->op		= op;
	t->args		= args;
	t->next		= 0;
	return t;
}

static Inst *add_inst (list, inst)
	Inst	*list;
	Inst	*inst;
{
	Inst	*t;
	for (t=list; t->next; t=t->next);
	t->next	= inst;
	return list;
}

static Cond *mk_cond (attr, op, value)
	Word	*attr;
	int	op;
	Word	*value;
{
	Cond	*t;
	t	= (Cond *) xmalloc (sizeof (Cond));
	t->attr	= attr;
	t->op	= op;
	t->value = value;
	t->nextop = 0;
	t->next	= 0;
	return t;
}

static Cond *add_cond (list, cond, op)
	Cond	*list;
	Cond	*cond;
	int	op;
{
	Cond	*t;
	for (t=list; t->next; t=t->next);
	t->next = cond;
	t->nextop = op;
	return list;
}

static Rule *mk_rule (cond, inst)
	Cond	*cond;
	Inst	*inst;
{
	Rule	*t;
	t	= (Rule *) xmalloc (sizeof (Rule));
	t->cond	= cond;
	t->inst	= inst;
	t->next	= 0;
	return t;
}

static Rule *add_rule (list, rule)
	Rule	*list;
	Rule	*rule;
{
	Rule	*t;
	for (t=list; t->next; t=t->next);
	t->next	= rule;
	return list;
}

int yyerror (msg)
	char *msg;
{
	extern char yytext[];
#ifdef FLEX_SCANNER
	fprintf (stderr, "line: %s at '%s'\n",
		msg, yytext);
#else
	fprintf (stderr, "line %d: %s at '%s'\n",
		yylineno, msg, yytext);
#endif
}
