/* pcdl_gram.y - Phone Category Definition Language Grammar -*- bison -*-
 *
 * This file is part of TUA.
 *
 *   Copyright (C) 1991,92,93  Lele Gaifax (lele@nautilus.sublink.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; 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

%{
  #include "pcdl.h"

  static pcdl_slot_list * days[7];

  pcdl_country current_country = { 0, 0, { 0, 0, 0, 0, 0, 0, 0 }};

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

%union {
  int ival;
  char * string;
  pcdl_country country;
}

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

%type <ival> hour
%type <country> country_defn
%type <string> default_country

%%

input		:
		| input country_defn {
    /* 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 && fstrcmp (default_country_name, $2.name) == 0)
      {
      	current_country = $2;
      	YYACCEPT;
      }

  #ifdef TEST  
    else
      {
      	pcdl_name_list * nl = $2.names;
      	pcdl_slot_list * sl;
      	int day;

      	printf ("Name: %s\n", $2.name);
      	while (nl)
	  {
	    printf ("\tcost %d='%s'\n", nl->name->cost, nl->name->name);
	    nl = nl->next;
	  }

      	for (day=0; day<7; day++)
	  {
	    printf ("\t* Setup for day %d\n", day);
	    sl = $2.slots[day];
	    while (sl)
	      {
	      	printf ("\tfrom %d to %d costs %d\n",
		      	sl->slot->from, sl->slot->to,
		      	sl->slot->cost);
	      	sl = sl->next;
	      }
	  }
      }
  #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	: PCKW_DEFAULT '=' PCKW_STRING ';' {
    $$ = $3;
  }
		  ;

country_defn	: PCKW_COUNTRY PCKW_STRING '=' defn_block {
    int day;

    $$.name = $2;
    $$.names = pcdl_names();
    for(day=0; day<7; day++)
      $$.slots[day] = days[day];

    pcdl_reset_slots();
    pcdl_reset_names();

    /* printf ("GRAM: name=%s names=%p \n", $2, $$.names); */
  }
		  ;

defn_block	: '{' defn_stmts '}'
		  ;

defn_stmts	:
		| defn_stmts defn_stmt1
		  ;

defn_stmt1	: name_stmt
		| day_stmt
		  ;

name_stmt	: PCKW_NAME PCKW_INT '=' PCKW_STRING ';' {
    pcdl_name * new = pcdl_create_new_name();

    new->cost = $2;
    new->name = $4;
    pcdl_insert_name (new);
    /* printf ("GRAM: found a name (%d=%s)\n", $2, $4); */
  }
		| PCKW_NAME PCKW_STRING '=' PCKW_STRING ';'  {
    int cost = pcdl_lookup_rate_symbol ($2, TRUE);
    pcdl_name * new = pcdl_create_new_name();

    new->cost = cost;
    new->name = $4;
    pcdl_insert_name (new);
  }
		  ;

day_stmt	: PCKW_DAY '=' day_block ';' {
    days[$1] = pcdl_slots();
    pcdl_reset_slots();
  }
		| PCKW_DAY '-' PCKW_DAY '=' day_block ';' {
    int day;

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

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

    for (day=$1; day<=$3; day++)
      days[day] = pcdl_slots();

    pcdl_reset_slots();
  }
		  ;

day_block	: '{' rates_stmts '}'
		  ;

rates_stmts	:
		| rates_stmts rates_stmt1
		  ;

rates_stmt1	: PCKW_RATE PCKW_INT '=' hour '-' hour ';' {
    pcdl_slot * new = pcdl_create_new_slot();

    new->from = $4;
    new->to = $6;
    new->cost = $2;

    pcdl_insert_slot (new);

    /* printf ("GRAM: found a slot (cost=%d from %d to %d)\n", $2, $4, $6); */
  }
		| PCKW_RATE PCKW_INT '=' hour '-' hour ',' hour '-' hour ';' {
  pcdl_slot * new = pcdl_create_new_slot();

  new->from = $4;
  new->to = $6;
  new->cost = $2;

  pcdl_insert_slot (new);

  new = pcdl_create_new_slot();

  new->from = $8;
  new->to = $10;
  new->cost = $2;

  pcdl_insert_slot (new);
}
		| PCKW_RATE PCKW_STRING '=' hour '-' hour ';' {
    pcdl_slot * new = pcdl_create_new_slot();
    int cost = pcdl_lookup_rate_symbol ($2, FALSE);

    new->from = $4;
    new->to = $6;
    new->cost = cost;

    pcdl_insert_slot (new);
  }
		| PCKW_RATE PCKW_STRING '=' hour '-' hour ',' hour '-' hour ';' {
    pcdl_slot * new = pcdl_create_new_slot();
    int cost = pcdl_lookup_rate_symbol ($2, FALSE);

    new->from = $4;
    new->to = $6;
    new->cost = cost;

    pcdl_insert_slot (new);

    new = pcdl_create_new_slot();

    new->from = $8;
    new->to = $10;
    new->cost = cost;

    pcdl_insert_slot (new);
  }
		  ;

hour		: PCKW_INT ':' PCKW_INT {
    $$ = $1*60 + $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);
}

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

int pcd_names_count = 0;

static void
DEFUN(yyerror, (s),
      CONST char * s)
{
  printf ("TUA:%s:%d: %s\n", pcd_filename, current_line, s);
}

int
DEFUN(parse_pcd_file, (pathname),
      CONST char * pathname)
{
  pcd_filename = pathname;
  if ((pcd_file = fopen (pathname, "r")) && yyparse() == 0)
    {
      pcdl_name_list * 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')
      current_line++;

  if (c == '#')
    {
      do
	{
	  c = getc(pcd_file);
	} while (c != EOF && c != '\n');
      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, "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)
    {
      pcdl_name_list * nl = current_country.names;
      pcdl_slot_list * sl;
      int day;
      
      printf ("Name: %s\n", current_country.name);
      while (nl)
	{
	  printf ("\tcost %d='%s'\n", nl->name->cost, nl->name->name);
	  nl = nl->next;
	}
      
      
      for (day=0; day<7; day++)
	{
	  printf ("\t* Setup for day %d\n", day);
	  sl = current_country.slots[day];
	  while (sl)
	    {
	      printf ("\tfrom %d to %d costs %d\n", sl->slot->from, sl->slot->to, sl->slot->cost);
	      sl = sl->next;
	    }
	}
    }
}
  
#endif /* TEST */
