/****************************************************************************
 * This file was derived from the CNCL library by the Pablo research group.
 *
 * The complete CNCL library is available at:
 *       http://www.comnets.rwth-aachen.de/doc/cncl.html
 *
 * The CNCL copyright notice is included here:
 *
 * |_|_|_  |_|_    |_    |_|_|_  |_               C O M M U N I C A T I O N
 *|_       |_  |_  |_  |_        |_                         N E T W O R K S
 *|_       |_  |_  |_  |_        |_                               C L A S S
 * |_|_|_  |_    |_|_    |_|_|_  |_|_|_|_                     L I B R A R Y
 *
 *
 *
 * Copyright (C) 1992-1998   Communication Networks
 *                           Aachen University of Technology
 *                           D-52056 Aachen
 *                           Germany
 *                           Email: cncl-adm@comnets.rwth-aachen.de
 *
 *
 * This library is free  software; you can  redistribute it and/or modify
 * it  under   the terms  of the GNU   Library  General Public License as
 * published by the  Free Software Foundation;  either  version 2 of  the
 * License, or  (at  your option)  any  later  version.  This  library 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 Library General Public
 * Library General Public License along with  this library; if not, write
 * to the Free Software Foundation,  675  Mass Ave, Cambridge, MA  02139,
 * USA.
 ****************************************************************************/

/* -*-C++-*-
 * 
 * Fuzzy Rules Compiler
 *
 * $Id: fuzzyparse.y,v 1.8 2003/03/03 16:39:42 swhitmor Exp $
 *
 * fuzzyparse.y - fuzzy rules parser (GNU bison)
 */

/* C definitions */
%{
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "fuzzysubs.h"

int yyerror(const char *);
int yylex(void);

void yyupdate(void);

extern FILE *hfile, *cfile, *in;
extern char *in_name;

/* Name for this fuzzy rule set */
char *rulebase;

/* Fuzzy sets */
struct Node *currentset;

/* Fuzzy variables */
enum VarUsed { NOTUSED = 0, USED };
struct Var {
	struct Node  node;
	struct List  setlist;
	enum VarUsed inused, outused;
} *currentvar;
struct List varlist = {NULL};

/* Fuzzy clauses */
enum VarType { UNDEF = 0, IN, OUT };
struct Clause {
	struct Node     node;
	struct Var     *var;
	enum   VarType  type;
	struct Node    *set;
} *currentclause;

/* Fuzzy rules */
int   rulenumber = 0;
struct Rule {
	struct List clauselist;
	double      certainty;
} currentrule = { {NULL}, 1.0 };

void initrule(struct Rule *r)
{
	r->clauselist.head = NULL;
	r->certainty = 1.0;
}
%}

/* Return values of the lexical scanner */
%union {
	double  num;
	char   *id;
       }

/* Tokens from lexical scanner */
/* Keywords */
%token Krulebase
%token Kset
%token Ktrapez
%token Ktriangle
%token Kvar
%token Kif
%token Kand
%token Kcertainty
%token Kis
/* Tokens with return value */
%token <id> Identifier
%token <num> Number

/* Fuzzy rules grammar */
%%

Program		: Krulebase RuleBaseName ';' Body
		  {
			struct Node *n = varlist.head;

			while (n)
			{
				freenamelist(&((struct Var *) n)->setlist);
				n = n->next; 
			}
			freenamelist(&varlist);
			/* swhitmor: removed 1/31/03 - rulebase used in yyupdate */
			/* free(rulebase); */
		  };

RuleBaseName	: Identifier
		  {
		      rulebase = strdup($1);

		      fprintf(hfile, "struct %s\n{\n\t%s(void);\n", $1, $1);
		      fprintf(hfile, "\t%s(const %s &);\n", $1, $1);
                      fprintf(hfile, "\tFl_bool updateFuzzySets(const char* filename);\n");
		      fprintf(hfile, "\tFlFRuleBase %sbase;\n", $1);

		      fprintf(cfile, "%s::%s(const %s &)\n{}\n\n", $1, $1, $1);
		      fprintf(cfile, "%s::%s(void)\n{\n", $1, $1);
		      fprintf(cfile, "\t%sbase.set_name(\"%s\");\n", $1, $1);
		  };

Body		: Body CommandLine |
		  CommandLine;

CommandLine	: Statement ';' |
                  Statement;
				  

Statement	: Definitions;

Definitions	: Kvar VarDefinition |
		  Kif  RuleDefinition;

VarDefinition	: NewVarId '(' Number ',' Number ')' '{' SetList '}'
		  {
		      struct Node *n = currentvar->setlist.head;
		      char *varname = (char *)currentvar->node.data;

		      fprintf(hfile, "\tFlFVar %s;\n", varname);
		      fprintf(cfile, "\t%s.init(\"%s\", %g, %g);\n",
			      varname, varname, $3, $5);

		      while (n)
		      {
			  fprintf(cfile, "\t%s.add_value_set(%s_%s);\n",
				  varname, varname, (char *)n->data);
			  n = n->next;
		      }
		  };

NewVarId	: Identifier
		  {
		      if (!(currentvar = (struct Var *)addnewname(&varlist,
				 sizeof(struct Var), $1)))
			{
				yyerror("redefinition of variable");
				YYERROR;
			}
		  };

SetList		: SetList NewSet  |
		  NewSet;

NewSet		: Kset SetDefinition ';';

SetDefinition	: Ktrapez SetTrapezDef |
		  Ktriangle SetTriangleDef;

SetTrapezDef	: NewSetId '(' Number ',' Number ',' Number ',' Number ')'
		  {
		      fprintf(hfile, "\tFlFSetTrapez %s_%s;\n",
			      (char *)currentvar->node.data,
			      (char *)currentset->data        );
		      fprintf(cfile, "\t%s_%s.init(\"%s\", %g, %g, %g, %g);\n",
			      (char *)currentvar->node.data,
			      (char *)currentset->data,
			      (char *)currentset->data, $3, $5, $7, $9);
		  };

NewSetId	: Identifier
		  {
			if (!(currentset = addnewname(&currentvar->setlist,
						sizeof(struct Node), $1)))
			{
				yyerror("set reused in variable");
				YYERROR;
			}
		  };

/* Triangles are treated as trapezoidals */
SetTriangleDef	: NewSetId '(' Number ',' Number ',' Number ')'
		  {
                      fprintf(hfile, "\t/* FlFSetTriangle converted into FlFSetTrapez */\n");
		      fprintf(hfile, "\tFlFSetTrapez %s_%s;\n",
			      (char *)currentvar->node.data,
			      (char *)currentset->data        );
                      fprintf(cfile, "\t/* FlFSetTriangle converted into FlFSetTrapez */\n");
		      fprintf(cfile, "\t%s_%s.init(\"%s\", %g, %g, %g, %g);\n",
			      (char *)currentvar->node.data,
			      (char *)currentset->data,
			      (char *)currentset->data, $3, $3, $5, $7);
		  };

RuleDefinition	: '(' LeftHandSide ')' '{' RightHandSide '}'
		  {
		      struct Clause *cl = (struct Clause *)
			  currentrule.clauselist.head;
		      char rulename[20];

		      sprintf(rulename,"rule%d",rulenumber++);

		      fprintf(hfile, "\tFlFRule %s;\n", rulename);
		      fprintf(cfile, "\t%s.certainty(%g);\n",
			      rulename, currentrule.certainty);
		      fprintf(cfile, "\t%sbase.add_rule(%s);\n",
			      rulebase, rulename);
		      
		      while (cl)
		      {
			  int         lhs     = (cl->type == IN);
			  struct Var *var     = cl->var;
			  char       *varname = (char *)var->node.data;
			  
			  fprintf(cfile,
				  "\t%s.add_%chs(new FlFClause(%s, %s_%s));\n",
				  rulename, (lhs ? 'l' : 'r'),
				  varname, varname,
				  (char *)cl->set->data);

			  if (lhs)
			  {
			      if (var->inused == NOTUSED)
			      {
				  fprintf(cfile, "\t%sbase.add_in_var(%s);\n",
					 rulebase, varname);
				  var->inused = USED;
			      }
			  }
			  else if (var->outused == NOTUSED)
			  {
			      fprintf(cfile, "\t%sbase.add_out_var(%s);\n",
				      rulebase, varname);
			      var->outused = USED;
			  }
			  
			  cl = (struct Clause *) cl->node.next;
		      }

		      freelist(&currentrule.clauselist);
		      initrule(&currentrule);
		  };

LeftHandSide	: LeftHandSide Kand ConditionP |
		  ConditionP;

ConditionP      : '(' ConditionP ')' |
                  Condition;

Condition	: VarIdentifier Kis SetIdentifier
		  {
			currentclause = (struct Clause *) 
					newnode(sizeof(struct Clause),
						NULL);
			currentclause->var  = currentvar;
			currentclause->type = IN;
			currentclause->set  = currentset;
			add(&currentrule.clauselist,
				(struct Node *) currentclause);
		  };

VarIdentifier	: Identifier
		  {
		      if (!(currentvar = (struct Var *)findname(&varlist,$1)))
		      {
			  yyerror("undefined variable");
			  YYERROR;
		      }
		  };

SetIdentifier	: Identifier
		  {
		      if (!(currentset = findname(&currentvar->setlist,$1)))
		      {
			  yyerror("set not associated with variable");
			  YYERROR;
		      }
		  };

RightHandSide	: RightHandSide AssignP ';' |
                  AssignP ';' ;

AssignP         : '(' AssignP ')' |
                  RuleOption |
                  Assign;

Assign		: VarIdentifier Kis SetIdentifier
		  {
		      currentclause = (struct Clause *)
			  newnode(sizeof(struct Clause), NULL);
		      currentclause->var  = currentvar;
		      currentclause->type = OUT;
		      currentclause->set  = currentset;
		      add(&currentrule.clauselist,
			  (struct Node *) currentclause);
		  };

RuleOption	: Kcertainty Kis Number
		  {
			currentrule.certainty = $3;
		  };

%%
/* Code to be appended to read the updated FuzzySets file */
void yyupdate(void){
	fprintf(cfile, "}\n\n");
	fprintf(cfile, "Fl_bool %s::updateFuzzySets(const char* filename)\n{\n", rulebase);
	fprintf(cfile, "\treturn %sbase.updateFuzzySets(filename);\n\n", rulebase);
	free(rulebase);
}

/* Interface to lexical scanner */
extern int lexline;
extern char *yytext;

/* Error routine */
int yyerror(const char *errtxt)
{
    fprintf(stderr, "%s:%d: %s, token '%s'\n", in_name, lexline, errtxt, yytext);
    return 0;
}
