/* (C) Copyright International Business Machines Corporation 23 January */
/* 1990.  All Rights Reserved. */
/*  */
/* See the file USERAGREEMENT distributed with this software for full */
/* terms and conditions of use. */
/* File: gentab.y */
/* Author: Andy Lowry */
/* SCCS Info: @(#)y_gentab.y	1.6 3/19/90 */


%{

#include "gentab.h"

static stringlist *operands;
static int curpos, selectpos;
static typerule *typerules;
static typestaterules tsrules;
static stringlist *exceptions;

/* Here is where copies of parsed symbols are placed */
char *symbol_string;

/* User routines to act on parsed operation specifications */
void init_checking_table();
void add_operation();
void done_checking_table();

/* local utility routines defined below */
void add_operand();
int operand_position();
intlist *add_int();
void add_inference_rule();
void add_assignment_rule();
void add_family_rule();
void add_precondition_rule();
void add_postcondition_rule();
void add_exception();
void set_special_postcondition();

/* special "operands list" used for variable operands lists */
stringlist variable_operands;

%}

%token T_IS T_IN T_SPECIAL T_VARIABLE T_SYMBOL

%union {
  char *string;
  stringlist *strings;
  int integer;
  intlist *ints;
}

%type <string>		operator
%type <integer>		operand_position
%type <string>		qualifier
%type <string>		operand
%type <string>		func
%type <string>		family
%type <ints>		operand_position_list
%type <ints>		operand_position_NElist
%type <string>		symbol

%start table

%%

table :
	{ init_checking_table(); }
	operation_s 
	{ done_checking_table(); } ;

operation :
	{ selectpos = curpos = -1;
	  operands = nil;
	  typerules = nil;
	  tsrules.type = NORMAL;
	  tsrules.preconditions = nil;
	  tsrules.post.postconditions = nil;
	  exceptions = nil;
	}
	operator p_operands
	qualifier type_rule_s typestate_rules ';'
	{ add_operation($2, operands, selectpos, $4, typerules,
			&tsrules, exceptions); } ;

operation_s : empty ;
operation_s : operation_s operation ;

operator : symbol
	{ $$ = $1; } ;

p_operands : '(' operand_name_list ')' ;
p_operands : T_VARIABLE
  	{ operands = &variable_operands; } ;

operand_name : operand
	{ curpos++; add_operand($1); } ;

operand_name : operand '*'
	{ selectpos = ++curpos; add_operand($1); } ;

operand_name_list : empty ;
operand_name_list : operand_name_NElist ;
operand_name_NElist : operand_name ;
operand_name_NElist : operand_name_NElist ',' operand_name ;

qualifier : symbol
	{ $$ = $1; } ;

type_rule : assignment_rule ;
type_rule : inference_rule ;
type_rule : family_rule ;

type_rule_s : empty ;
type_rule_s : type_rule_s type_rule ;


inference_rule :
	operand_position T_IS func '(' operand_position ')'
	{ add_inference_rule($1, $3, $5); } ;

assignment_rule :
	operand_position T_IS func '(' ')'
	{ add_assignment_rule($1, $3); } ;

family_rule :
	operand_position T_IN family
	{ add_family_rule($1, $3); } ;

typestate_rules : '(' precondition_rule_list ')' postcondition 
       '(' exception_list ')' ;

precondition_rule : func '(' operand_position_list ')'
	{ add_precondition_rule($1, $3); } ;

precondition_rule_list : empty ;
precondition_rule_list : precondition_rule_NElist ;
precondition_rule_NElist : precondition_rule ;
precondition_rule_NElist : precondition_rule_NElist ',' precondition_rule ;

postcondition : normal_postcondition ;
postcondition : special_postcondition ;

normal_postcondition : '(' postcondition_rule_list ')' ;

postcondition_rule : func '(' operand_position_list ')'
	{ add_postcondition_rule($1, $3); } ;

postcondition_rule_list : empty ;
postcondition_rule_list : postcondition_rule_NElist ;
postcondition_rule_NElist : postcondition_rule ;
postcondition_rule_NElist : postcondition_rule_NElist ',' postcondition_rule ;

exception : symbol
	{ add_exception($1); } ;

exception_list : empty ;
exception_list : exception_NElist ;
exception_NElist : exception ;
exception_NElist : exception_NElist ',' exception ;

special_postcondition : T_SPECIAL func
	{ set_special_postcondition($2); } ;

operand_position : operand
	{ $$ = operand_position($1); } ;

operand_position_list : empty
	{ $$ = nil; } ;
operand_position_list : operand_position_NElist
	{ $$ = $1; } ;
operand_position_NElist : operand_position
	{ $$ = add_int($1, nil); } ;
operand_position_NElist : operand_position_NElist ',' operand_position
	{ $$ = add_int($3, $1); } ;

operand : symbol
	{ $$ = $1; } ;
func : symbol
	{ $$ = $1; } ;
family : symbol
	{ $$ = $1; } ;

symbol : T_SYMBOL
	{ $$ = symbol_string; };

empty : ;

%%

/* Generic support routines for rule actions */

#include <stdio.h>
#include <assert.h>

char *safe_malloc();

/* add a given operand name to the operands list */
static void
add_operand(name)
char *name;
{
  stringlist *namecell, *lastoperand;

  namecell = (stringlist *) safe_malloc(sizeof(stringlist));
  namecell->value = name;
  namecell->next = nil;
  if (operands == nil)
    operands = namecell;
  else {
    for (lastoperand = operands; lastoperand->next != nil;
	 lastoperand = lastoperand->next)
      ;
    lastoperand->next = namecell;
  }
}

/* Find the position of the given operand in the operands list.  First */
/* operand position is 1 */
static int
operand_position(name)
char *name;
{
  stringlist *operand;
  int pos;

  for (operand = operands, pos = 0; operand != nil;
       operand = operand->next, pos++)
    if (strcmp(operand->value, name) == 0)
      return(pos);
  fprintf(stderr, "Unknown operand: %s", name);
  exit(1);
}


/* Add an integer to the end of a list of integers */
static intlist *
add_int(new,list)
int new;
intlist *list;
{
  intlist *intcell, *lastint;

  intcell = (intlist *) safe_malloc(sizeof(intlist));
  intcell->value = new;
  intcell->next = nil;
  if (list == nil)
    return(intcell);
  else {
    for (lastint = list ; lastint->next != nil; lastint = lastint->next)
      ;
    lastint->next = intcell;
    return(list);
  }
}


/* Add an inference rule to the list of typerules parsed so far for */
/* this operation */
static void
add_inference_rule(target, func, source)
int target, source;
char *func;
{
  typerule *rule;
  void add_typerule();

  rule = (typerule *) safe_malloc(sizeof(typerule));
  rule->type = INFERENCE;
  rule->rule.inf.target = target;
  rule->rule.inf.func = func;
  rule->rule.inf.source = source;
  rule->next = nil;
  
  add_typerule(rule);
}

/* Add an assignment rule to the list of typerules parsed so far for */
/* this operation */
static void
add_assignment_rule(target, func)
int target;
char *func;
{
  typerule *rule;
  void add_typerule();

  rule = (typerule *) safe_malloc(sizeof(typerule));
  rule->type = ASSIGNMENT;
  rule->rule.asgn.target = target;
  rule->rule.asgn.func = func;
  rule->next = nil;
  
  add_typerule(rule);
}

/* Add a family rule to the list of typerules parsed so far for this */
/* operation */
static void
add_family_rule(target, family)
int target;
char *family;
{
  typerule *rule;
  void add_typerule();

  rule = (typerule *) safe_malloc(sizeof(typerule));
  rule->type = FAMILY;
  rule->rule.fam.target = target;
  rule->rule.fam.family = family;
  rule->next = nil;

  add_typerule(rule);
}

/* Add a type rule to the list for this operation */
static void
add_typerule(rule)
typerule *rule;
{
  typerule *lastrule;

  if (typerules == nil)
    typerules = rule;
  else {
    for (lastrule = typerules; lastrule->next != nil;
	 lastrule = lastrule->next)
      ;
    lastrule->next = rule;
  }
}

/* Add a precondition rule to the list of preconditions parsed so far */
/* for this operation */
static void
add_precondition_rule(func, operands)
char *func;
intlist *operands;
{
  tsrule *rule, *lastrule;

  rule = (tsrule *) safe_malloc(sizeof(tsrule));
  rule->func = func;
  rule->operands = operands;
  rule->next = nil;
  if (tsrules.preconditions == nil)
    tsrules.preconditions = rule;
  else {
    for (lastrule = tsrules.preconditions; lastrule->next != nil;
	 lastrule = lastrule->next)
      ;
    lastrule->next = rule;
  }
}

/* Add a postcondition rule to the list for this operation */
static void
add_postcondition_rule(func, operands)
char *func;
intlist *operands;
{
  tsrule *rule, *lastrule;

  rule = (tsrule *) safe_malloc(sizeof(tsrule));
  rule->func = func;
  rule->operands = operands;
  rule->next = nil;

  if (tsrules.post.postconditions == nil)
    tsrules.post.postconditions = rule;
  else {
    for (lastrule = tsrules.post.postconditions; lastrule->next != nil;
	 lastrule = lastrule->next)
      ;
    lastrule->next = rule;
  }
}

/* Add the given exception name to the list for this operation */
static void
add_exception(name)
char *name;
{
  stringlist *newcell, *lastcell;

  newcell = (stringlist *) safe_malloc(sizeof(stringlist));
  newcell->value = name;
  newcell->next = nil;

  if (exceptions == nil)
    exceptions = newcell;
  else {
    for (lastcell = exceptions; lastcell->next != nil;
	 lastcell = lastcell->next)
      ;
    lastcell->next = newcell;
  }
}

/* Set the postcondition rule for the current operation to be the */
/* given special rule name */
static void
set_special_postcondition(name)
char *name;
{
  tsrules.type = SPECIAL;	/* type was assumed to be NORMAL */
  tsrules.post.special = name;
}

/* A malloc that exits when it fails */
char *
safe_malloc(size)
int size;
{
  char *malloc();
  char *stg;

  stg = malloc(size);
  assert(stg != nil);
  return(stg);
}
