/* pcdl_gram.y - Phone Category Definition Language Grammar
 *
 * This file is part of TUA.
 *
 *   Copyright (C) 1991,96  Lele Gaifax
 *
 *   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; either version 2 of the license, or (at
 *   your option) any later version.
 *
 *   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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

%{
  #include "tua.h"
  #include "pcdl.h"

  pcdl_country current_country;

  void EXFUN(yyerror, (CONST char * s));
  static int EXFUN(yylex, (NOARGS));

  static CONST char * pcd_filename;
  static FILE * pcd_file;
  static int pcd_current_line = 0;

  int pcd_names_count = 0;

  int pcd_list_countries = FALSE;
  %}

%union {
  int ival;
  float fval;
  char * string;
  pcdl_slots * slots;
}

%token PCKW_COUNTRY
%token <ival> PCKW_INT
%token <string> PCKW_STRING
%token <ival> PCKW_DAY
%token PCKW_RATE
%token PCKW_NAME
%token PCKW_DEFAULT
%token PCKW_CURRENCY
%token PCKW_DECIMALS
%token PCKW_COST

%type <ival> hour
%type <string> default_country
%type <slots> slots slot rates rate day_block
%type <fval> float_number

%%

input		:
	        | input country
		  {
    /* Ok, we have a complete country definition. First of all destroy the
     * symbol table, since we are not going to use it anymore. Then, if the
     * country name matches the default one (specificated by either --country
     * option or with the "default" clause), store it and return.
     * Otherwise go on with the next */

    pcdl_destroy_symbol_list ();
    if (default_country_name && !pcd_list_countries && strcmp (default_country_name, current_country.name) == 0)
      {
      	YYACCEPT;
      }

  #ifdef TEST  
    else
      {
      	printf ("Found a country:\n");
      	pcdl_print_country (&current_country);
      }
  #endif
  }
		| input default_country
		   {
    /* if the country is not yet set (via --country option) */
    if (default_country_name == 0)
      default_country_name = $2;
  }
 		  ;

/* default = "country_name"; */
default_country	: PCKW_DEFAULT '=' PCKW_STRING ';'
		   {
    $$ = $3;
  }
		  ;

/* country "italy" = <defn_block> */
country		: PCKW_COUNTRY reset_country PCKW_STRING '=' defn_block
		   {
    current_country.name = $3;

    if (pcd_list_countries)
      printf ("%s\n", $3);
  }
		  ;

/* whenever a "country" stmt is found, reset the current_country info */
reset_country	: /* empty */
		  {
    bzero (&current_country, sizeof (current_country));
  }
		  ;

defn_block	: '{' defn_stmts '}'
		  ;

defn_stmts	: defn_stmt1
		| defn_stmts defn_stmt1
		  ;

defn_stmt1	: name
		| day
		| currency
		| decimals
		| cost
		  ;

/* name 1 = "name_1";
 * or
 * name FOO = "fooname"; */		 
name		: PCKW_NAME PCKW_STRING '=' PCKW_STRING ';'
		   {
    int cost = pcdl_lookup_rate_symbol ($2, TRUE);
    pcdl_names * new = pcdl_create_new_name();

    new->cost = cost;
    new->name = $4;
    new->next = 0;
    current_country.names = pcdl_insert_name (new, current_country.names);
  }
		  ;

cost		: PCKW_COST PCKW_STRING '=' float_number ';'
 		  {
    int cost = pcdl_lookup_rate_symbol ($2, FALSE);
    pcdl_names * names = current_country.names;

    while (names && names->cost != cost)
      names = names->next;

    if (names)
      {
        names->cost_per_unit = 0.0;
        names->seconds_per_unit = 0.0;
        names->call_cost = $4;
      }
  }
                | PCKW_COST PCKW_STRING '=' float_number '/' float_number ';'
		  {
    int cost = pcdl_lookup_rate_symbol ($2, FALSE);
    pcdl_names * names = current_country.names;

    while (names && names->cost != cost)
      names = names->next;

    if (names)
      {
        names->cost_per_unit = (float) $4;
        names->seconds_per_unit = $6;
        names->call_cost = 0.0;
      }
  }
                | PCKW_COST PCKW_STRING '=' float_number ',' float_number '/' float_number  ';'
 		  {
    int cost = pcdl_lookup_rate_symbol ($2, FALSE);
    pcdl_names * names = current_country.names;

    while (names && names->cost != cost)
      names = names->next;

    if (names)
      {
        names->cost_per_unit = (float) $6;
        names->seconds_per_unit = $8;
        names->call_cost = (float) $4;
      }
  }    
		  ;


float_number	: PCKW_INT
		  {
    $$ = (float) $1;
  }
		| PCKW_INT '.' PCKW_INT
		  {
    extern double EXFUN(atof, (char *));
    char buffer[50];

    sprintf (buffer, "%d.%d", $1, $3);
    $$ = atof (buffer);
  }
		  ;

/* sunday = <day_block>;
 * or
 * monday-friday = <day_block>; */		  
day		: PCKW_DAY '=' day_block ';'
		   {
    current_country.slots[$1] = pcdl_merge_slots (current_country.slots[$1], $3);
  }
		| PCKW_DAY '-' PCKW_DAY '=' day_block ';'
		   {
    int day;

    if ($1 > $3)
      {
      	int swap = $1;

      	$1 = $3;
      	$3 = swap;
      }

    for (day=$1; day<=$3; day++)
      current_country.slots[day] = pcdl_merge_slots (current_country.slots[day], $5);
  }
		  ;

day_block	: '{' rates '}'
		   {$$ = $2; }
		  ;

rates		: rate
		| rates rate
		  {
  $$ = pcdl_merge_slots ($1, $2);
}
		  ;

/* rate 1 = <slots>
 * or
 * rate FOO = <slots> */
rate		: PCKW_RATE PCKW_STRING '=' slots ';'
		  {
    int cost = pcdl_lookup_rate_symbol ($2, FALSE);
    pcdl_slots * sl = $4;

    while (sl)
      {
      	sl->cost = cost;
      	sl = sl->next;
      }
    $$ = $4;
  }
		  ;

slots		: slot
		   {
    $$ = $1;
  }
		| slots ',' slot
		   {
    $$ = pcdl_insert_slot ($3, $1);
  }
		  ;

slot		: hour '-' hour
		   {
    $$ = pcdl_create_new_slot();

    if ($1 > $3)
      {
      	/* 19:01-07:00 case: trasform in 19:01-24:00, 00:01-07:00 */
      	pcdl_slots * other;

      	$$->from = $1;
      	$$->to = 24*60;
      	$$->cost = 0;
      	$$->definition_line = pcd_current_line+1;
      	$$->next = other = pcdl_create_new_slot();
      	other->from = 1;
      	other->to = $3;
      	other->cost = 0;
      	other->definition_line = pcd_current_line+1;
      	other->next = 0;
      }
    else
      {
      	$$->from = $1;
      	$$->to = $3;
      	$$->cost = 0;
      	$$->definition_line = pcd_current_line+1;
      	$$->next = 0;
      }
  }
		  ;

hour		: PCKW_INT ':' PCKW_INT
		   {
    $$ = $1*60 + $3;
  }
		| PCKW_INT '.' PCKW_INT
		   {
    $$ = $1*60 + $3;
  }
		  ;

currency	: PCKW_CURRENCY '=' PCKW_STRING ';'
		  {
    current_country.currency = savestring ($3);
  }
		  ;

decimals	: PCKW_DECIMALS '=' PCKW_INT ';'

		   {
    current_country.decimals = $3;
  }
		  ;

%%
#include <ctype.h>

#define SYM_LENGTH 30

#if defined (__GNUC__) && !defined(__STRICT_ANSI__)
__inline__
#endif
int
DEFUN (strequ, (s1, s2),
       char * s1 AND char * s2)
{
  return (strcasecmp (s1, s2) == 0);
}

void
DEFUN(yyerror, (s),
      CONST char * s)
{
  fprintf (stderr, "TUA:%s:%d: %s\n", pcd_filename, pcd_current_line, s);
}

int
DEFUN(parse_pcd_file, (pathname),
      CONST char * pathname)
{
  pcd_filename = pathname;
  if ((pcd_file = fopen (pathname, "r")) && yyparse() == 0)
    {
      pcdl_names * nl = current_country.names;

      pcd_names_count = 0;
      while (nl)
	{
	  pcd_names_count++;
	  nl = nl->next;
	}
      fclose (pcd_file);
      
      return OK;
    }
  else
    return ERROR;
}

static int
DEFUN_VOID(yylex)
{
  int c;

read_again:  
  while ((c = getc(pcd_file)) == ' ' || c == '\t' || c == '\n')
    if (c == '\n')
      pcd_current_line++;

  if (c == '#')
    {
      do
	{
	  c = getc(pcd_file);
	} while (c != EOF && c != '\n');
      pcd_current_line++;
      
      if (c != EOF)
	goto read_again;
    }
  
  if (c == EOF)
    return 0;

  if (isdigit (c))
    {
      ungetc (c, pcd_file);
      fscanf (pcd_file, "%d", &yylval.ival);
      return PCKW_INT;
    }

  if (isalpha (c))
    {
      char symbol[SYM_LENGTH];
      int i = 0;

      do
	{
	  symbol[i++] = c;
	  if (i == SYM_LENGTH)
	    {
	      fprintf (stderr, _("error: symbol %.30s... too long!\n"), symbol);
	      exit (1);
	    }
	  c = getc(pcd_file);
	} while (c != EOF && isalnum (c));

      ungetc (c, pcd_file);
      symbol[i] = 0;

      if (strequ (symbol, "COUNTRY")) return PCKW_COUNTRY;
      else if (strequ (symbol, "NAME")) return PCKW_NAME;
      else if (strequ (symbol, "RATE")) return PCKW_RATE;
      else if (strequ (symbol, "DEFAULT")) return PCKW_DEFAULT;
      else if (strequ (symbol, "CURRENCY")) return PCKW_CURRENCY;
      else if (strequ (symbol, "DECIMALS")) return PCKW_DECIMALS;
      else if (strequ (symbol, "COST")) return PCKW_COST;
      else if (strequ (symbol, "SUN") || strequ (symbol, "SUNDAY"))
	{
	  yylval.ival = 0;
	  return PCKW_DAY;
	}
      else if (strequ (symbol, "MON") || strequ (symbol, "MONDAY"))
	{
	  yylval.ival = 1;
	  return PCKW_DAY;
	}
      else if (strequ (symbol, "TUE") || strequ (symbol, "TUESDAY"))
	{
	  yylval.ival = 2;
	  return PCKW_DAY;
	}
      else if (strequ (symbol, "WED") || strequ (symbol, "WEDNESDAY"))
	{
	  yylval.ival = 3;
	  return PCKW_DAY;
	}
      else if (strequ (symbol, "THU") || strequ (symbol, "THURSDAY"))
	{
	  yylval.ival = 4;
	  return PCKW_DAY;
	}
      else if (strequ (symbol, "FRI") || strequ (symbol, "FRIDAY"))
	{
	  yylval.ival = 5;
	  return PCKW_DAY;
	}
      else if (strequ (symbol, "SAT") || strequ (symbol, "SATURDAY"))
	{
	  yylval.ival = 6;
	  return PCKW_DAY;
	}
      else
	{
	  yylval.string = savestring (symbol);
	  return PCKW_STRING;
	}
    }

  if (c == '"')
    {
      char string[SYM_LENGTH];
      int i;

      for (i=0, c = getc(pcd_file); c != EOF && c != '"'; i++, c = getc(pcd_file))
	string[i] = c;

      string[i] = 0;
      yylval.string = savestring (string);
      return PCKW_STRING;
    }

  return c;
}

#if TEST

main()
{
  pcd_file = stdin;
  if (yyparse() == 0)
    {
      printf ("But this is the right one!\n");
      pcdl_print_country (&current_country);
    }
  exit (0);
}
  
#endif /* TEST */
