%{
/*
    This file is part of the FElt finite element analysis package.
    Copyright (C) 1993 Jason I. Gobat and Darren C. Atkinson

    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.
*/

/************************************************************************
 * File:	parser.y						*
 *									*
 * Description:	This file contains the yacc specification for the	*
 *		input file parser.					*
 ************************************************************************/

# include <stdio.h>
# include <string.h>
# include <math.h>
# include "problem.h"
# include "allocate.h"
# include "objects.h"
# include "error.h"

# define ln problem.line
# define MNPE MaxNodesPerElement

static double		  lastx;		/* last x coordinate	    */
static double		  lasty;		/* last y coordinate	    */
static double		  lastz;		/* last z coordinate	    */
static char		 *lastconstraint;	/* name of last constraint  */
static char		 *lastmaterial;		/* name of last material    */
static int		  intarray [MNPE];	/* integer array	    */
static int		 *intptr;		/* pointer into array	    */
static Pair		  pairarray [MNPE];	/* pair array		    */
static Pair		 *pairptr;		/* pointer into array	    */
static Node		  node;			/* current node		    */
static Element		  element;		/* current element	    */
static Material		  material;		/* current material	    */
static Force		  force;		/* current force	    */
static Constraint	  constraint;		/* current constraint       */
static Distributed        distributed;		/* current distributed load */
static Definition	  definition;		/* current definition	    */
static struct node	  dummy_node;		/* dummy node		    */
static struct element     dummy_element;	/* dummy element	    */
static struct force	  dummy_force;		/* dummy force		    */
static struct material    dummy_material;	/* dummy material	    */
static struct constraint  dummy_constraint;	/* dummy constraint	    */
static struct distributed dummy_distributed;	/* dummy distributed load   */
%}

%union {
    double d;
    int	   i;
    char  *s;
    Pair   p;
}


%left	'+' '-'
%left	'*' '/'
%right	UNARY

%token	DOUBLE INTEGER STRING BOLINTEGER BOLSTRING
%token	PROBLEM NODES ELEMENTS MATERIALS FORCES CONSTRAINTS DISTRIBUTED END
%token	TITLE_EQ NODES_EQ ELEMENTS_EQ
%token	MATERIAL_EQ FORCE_EQ CONSTRAINT_EQ DIR_EQ VALUES_EQ LOAD_EQ
%token	X_EQ Y_EQ Z_EQ
%token	E_EQ IX_EQ IY_EQ IZ_EQ A_EQ J_EQ G_EQ T_EQ RHO_EQ NU_EQ KAPPA_EQ
%token	FX_EQ FY_EQ FZ_EQ MX_EQ MY_EQ MZ_EQ
%token	TX_FIXED TY_FIXED TZ_FIXED RX_FIXED RY_FIXED RZ_FIXED
%token	TX_FREE  TY_FREE  TZ_FREE  RX_FREE  RY_FREE  RZ_FREE 
%token	TX_EXPR  TY_EXPR  TZ_EXPR  RX_EXPR  RY_EXPR  RZ_EXPR 
%token	RX_HINGE RY_HINGE RZ_HINGE
%token  SIN COS TAN SQRT

%type	<s> STRING BOLSTRING ELEMENTS string_list
%type	<d> DOUBLE expression
%type	<i> INTEGER BOLINTEGER DIR_EQ
%type	<p> pair
%%

specification
	: description definition_list END
	    {
		if (problem.numerrors) {
		    error ("%d error(s) found in input", problem.numerrors);
		    return problem.numerrors;
		}

		if (resolvenames ( ))
		    return problem.numerrors;
	    }
	;


description
	: optional_info
	    {
		problem.node_tree        = TreeCreate (node_cmp);
		problem.element_tree     = TreeCreate (element_cmp);
		problem.force_tree       = TreeCreate (force_cmp);
		problem.material_tree    = TreeCreate (material_cmp);
		problem.constraint_tree  = TreeCreate (constraint_cmp);
		problem.distributed_tree = TreeCreate (distributed_cmp);

		lastx = lasty = lastz = 0;
		lastconstraint = lastmaterial = NULL;

		memset (&dummy_node, 0, sizeof (struct node));
		memset (&dummy_element, 0, sizeof (struct element));
		memset (&dummy_force, 0, sizeof (struct force));
		memset (&dummy_material, 0, sizeof (struct material));
		memset (&dummy_constraint, 0, sizeof (struct constraint));
		memset (&dummy_distributed, 0, sizeof (struct distributed));
	    }
	;


optional_info
	: PROBLEM info_list
	| /* empty */
	    {
		problem.numnodes = 0;
		problem.numelements = 0;
		problem.title = NULL;
	    }
	;


info_list
	: info_list info
	|
	;


info
	: NODES_EQ INTEGER	{problem.numnodes = $2;}

	| ELEMENTS_EQ INTEGER	{problem.numelements = $2;}

	| TITLE_EQ STRING	{problem.title = $2;}
	;


definition_list
	: definition_list definition
	|
	;


definition
	: NODES node_list
	| MATERIALS material_list
	| FORCES force_list
	| CONSTRAINTS constraint_list
	| DISTRIBUTED distributed_list
	| ELEMENTS
	    {definition = defnlookup ($1); Deallocate ($1);
	     if (definition == NULL) return -1;}
	  element_list
	;


node_list
	: node_list node
	|
	;


element_list
	: element_list element
	|
	;


material_list
	: material_list material
	|
	;


force_list
	: force_list force
	|
	;


constraint_list
	: constraint_list constraint
	|
	;


distributed_list
	: distributed_list distributed
	|
	;


node
	: BOLINTEGER
	    {
		Item found;


		if ($1 < 1 || $1 > problem.numnodes) {
		    error ("node number %d is illegal (near line %d)", $1, ln);
		    node = &dummy_node;
		    problem.numerrors ++;
		    break;
		}

		node = CreateNode ($1);
		found = TreeInsert (problem.node_tree, node);

		if ((Node) found != node) {
		    error ("node number %d is repeated (near line %d)", $1, ln);
		    DestroyNode (node);
		    node = &dummy_node;
		    problem.numerrors ++;
		    break;
		}

		node -> x	   = lastx;
		node -> y	   = lasty;
		node -> z	   = lastz;
		node -> constraint = (Constraint) lastconstraint;
	    }
	  node_arg_list
	| error
	;


element
	: BOLINTEGER
	    {
	        Item found;


		if ($1 < 1 || $1 > problem.numelements) {
		    error ("element number %d is illegal (near line %d)",$1,ln);
		    element = &dummy_element;
		    problem.numerrors ++;
		    break;
		}

		element = CreateElement ($1, definition);
		found = TreeInsert (problem.element_tree, element);

		if ((Element) found != element) {
		    error("element number %d is repeated (near line %d)",$1,ln);
		    DestroyElement (element);
		    element = &dummy_element;
		    problem.numerrors ++;
		    break;
		}

		element -> material = (Material) lastmaterial;
	    }
	  element_arg_list
	| error
	;


material
	: BOLSTRING
	    {
		Item found;


		material = CreateMaterial ($1);
		found = TreeInsert (problem.material_tree, material);

		if ((Material) found != material) {
		    error ("material %s is previously defined", $1);
		    DestroyMaterial (material);
		    material = &dummy_material;
		    problem.numerrors ++;
		}
	    }
	  material_arg_list
	| error
	;


force
	: BOLSTRING
	    {
		Item found;


		force = CreateForce ($1);
		found = TreeInsert (problem.force_tree, force);

		if ((Force) found != force) {
		    error ("force %s is previously defined", $1);
		    DestroyForce (force);
		    force = &dummy_force;
		    problem.numerrors ++;
		}
	    }
	  force_arg_list
	| error
	;


constraint
	: BOLSTRING
	    {
		Item found;


		constraint = CreateConstraint ($1);
		found = TreeInsert (problem.constraint_tree, constraint);

		if ((Constraint) found != constraint) {
		    error ("constraint %s is previously defined", $1);
		    DestroyConstraint (constraint);
		    constraint = &dummy_constraint;
		    problem.numerrors ++;
		}
	    }
	  constraint_arg_list
	| error
	;


distributed
	: BOLSTRING
	    {
		Item found;


		distributed = CreateDistributed ($1, 0);
		found = TreeInsert (problem.distributed_tree, distributed);

		if ((Distributed) found != distributed) {
		    error ("distributed load %s is previously defined", $1);
		    DestroyDistributed (distributed);
		    distributed = &dummy_distributed;
		    problem.numerrors ++;
		}
	    }
	  distributed_arg_list
	| error
	;


node_arg_list
	: node_arg_list node_arg
	|
	;


element_arg_list
	: element_arg_list element_arg
	|
	;


material_arg_list
	: material_arg_list material_arg
	|
	;


force_arg_list
	: force_arg_list force_arg
	|
	;


constraint_arg_list
	: constraint_arg_list constraint_arg
	|
	;


distributed_arg_list
	: distributed_arg_list distributed_arg
	|
	;


node_arg
	: X_EQ expression	{lastx = node -> x = $2;}

	| Y_EQ expression	{lasty = node -> y = $2;}

	| Z_EQ expression	{lastz = node -> z = $2;}

	| FORCE_EQ STRING	{node -> force = (Force) $2;}

	| CONSTRAINT_EQ STRING	{node -> constraint = (Constraint) $2;
				 lastconstraint = $2;}
	;


element_arg
	: NODES_EQ '[' integer_list ']'
	    {
		int i, size;


		if (element == &dummy_element)
		    break;

		size = intptr - intarray;

		if (size != element -> definition -> numnodes) {
		    error ("incorrect number of nodes for element %d",
			    element -> number);
		    problem.numerrors ++;
		    break;
		}

		for (i = 1; i <= size; i ++)
		    element -> node [i] = (Node) intarray [i - 1];
	    }

	| MATERIAL_EQ STRING
	    {
		element -> material = (Material) (lastmaterial = $2);
	    }

	| LOAD_EQ string_list
	;


material_arg
	: E_EQ expression	{material -> E = $2;}

	| IX_EQ expression	{material -> Ix = $2;}

	| IY_EQ expression	{material -> Iy = $2;}

	| IZ_EQ expression	{material -> Iz = $2;}

	| A_EQ expression	{material -> A = $2;}

	| J_EQ expression	{material -> J = $2;}

	| G_EQ expression	{material -> G = $2;}

	| T_EQ expression	{material -> t = $2;}

	| RHO_EQ expression	{material -> rho = $2;}

	| NU_EQ expression	{material -> nu = $2;}

	| KAPPA_EQ expression	{material -> kappa = $2;}
	;


force_arg
	: FX_EQ expression	{force -> force [Fx] = $2;}

	| FY_EQ expression	{force -> force [Fy] = $2;}

	| FZ_EQ expression	{force -> force [Fz] = $2;}

	| MX_EQ expression	{force -> force [Mx] = $2;}

	| MY_EQ expression	{force -> force [My] = $2;}

	| MZ_EQ expression	{force -> force [Mz] = $2;}
	;


constraint_arg
	: TX_FIXED
	    {
		constraint -> constraint [Tx] = 1;
		constraint -> dx [Tx] = 0;
	    }

	| TY_FIXED
	    {
		constraint -> constraint [Ty] = 1;
		constraint -> dx [Ty] = 0;
	    }

	| TZ_FIXED
	    {
		constraint -> constraint [Tz] = 1;
		constraint -> dx [Tz] = 0;
	    }

	| RX_FIXED
	    {
		constraint -> constraint [Rx] = 1;
		constraint -> dx [Rx] = 0;
	    }

	| RY_FIXED
	    {
		constraint -> constraint [Ry] = 1;
		constraint -> dx [Ry] = 0;
	    }

	| RZ_FIXED
	    {
		constraint -> constraint [Rz] = 1;
		constraint -> dx [Rz] = 0;
	    }

	| TX_FREE
	    {
		constraint -> constraint [Tx] = 0;
	    }

	| TY_FREE
	    {
		constraint -> constraint [Ty] = 0;
	    }

	| TZ_FREE
	    {
		constraint -> constraint [Tz] = 0;
	    }

	| RX_FREE
	    {
		constraint -> constraint [Rx] = 0;
	    }

	| RY_FREE
	    {
		constraint -> constraint [Ry] = 0;
	    }

	| RZ_FREE
	    {
		constraint -> constraint [Rz] = 0;
	    }

	| RX_HINGE
	    {
		constraint -> constraint [Rx] = 'h';
	    }

	| RY_HINGE
	    {
		constraint -> constraint [Ry] = 'h';
	    }

	| RZ_HINGE
	    {
		constraint -> constraint [Rz] = 'h';
	    }

	| TX_EXPR expression
	    {
		constraint -> constraint [Tx] = 1;
		constraint -> dx [Tx] = $2;
	    }

	| TY_EXPR expression
	    {
		constraint -> constraint [Ty] = 1;
		constraint -> dx [Ty] = $2;
	    }

	| TZ_EXPR expression
	    {
		constraint -> constraint [Tz] = 1;
		constraint -> dx [Tz] = $2;
	    }

	| RX_EXPR expression
	    {
		constraint -> constraint [Rx] = 1;
		constraint -> dx [Rx] = $2;
	    }

	| RY_EXPR expression
	    {
		constraint -> constraint [Ry] = 1;
		constraint -> dx [Ry] = $2;
	    }

	| RZ_EXPR expression
	    {
		constraint -> constraint [Rz] = 1;
		constraint -> dx [Rz] = $2;
	    }
	;


distributed_arg
	: DIR_EQ
	    {
		distributed -> direction = $1;
	    }


	| VALUES_EQ pair_list
	    {
		int i, size;


		if (distributed == &dummy_distributed)
		    break;

		size = pairptr - pairarray;

		if (!(distributed -> value = Allocate (Pair, size)))
		    Fatal ("unable to allocate memory for pairs");

		UnitOffset (distributed -> value);
		distributed -> nvalues = size;

		for (i = 1; i <= size; i ++)
		    distributed -> value [i] = pairarray [i - 1];
	    }
	;


string_list
	: string_list STRING
	    {
		if (element -> numdistributed == 3) {
		    error ("element %d has too many loads", element -> number);
		    break;
		}

		element -> distributed [++ element -> numdistributed] =
							(Distributed) $2;
	    }

	| STRING
	    {
		element -> distributed [++ element -> numdistributed] =
							(Distributed) $1;
	    }
	;


integer_list
	: integer_list INTEGER
	    {
		if ($2 < 0 || $2 > problem.numnodes) {
		    error ("node number %d is illegal (near line %d)", $2, ln);
		    problem.numerrors ++;
		}

		*intptr ++ = $2;
	    }

	| INTEGER
	    {
		if ($1 < 0 || $1 > problem.numnodes) {
		    error ("node number %d is illegal (near line %d)", $1, ln);
		    problem.numerrors ++;
		}

		intptr = intarray;
		*intptr ++ = $1;
	    }
	;


pair_list
	: pair_list pair
	    {
		if ($2.node < 1 || $2.node > problem.numnodes) {
		    error ("node number %d is illegal (near line %d)",
			   $2.node, ln);
		    problem.numerrors ++;
		}

		*pairptr ++ = $2;
	    }

	| pair
	    {
		if ($1.node < 1 || $1.node > problem.numnodes) {
		    error ("node number %d is illegal (near line %d)",
			   $1.node, ln);
		    problem.numerrors ++;
		}

		pairptr = pairarray;
		*pairptr ++ = $1;
	    }
	;


pair
	: '(' INTEGER expression ')'
	    {
		$$.node = $2;
		$$.magnitude = $3;
	    }
	;


expression
	: expression '+' expression	{$$ = $1 + $3;}

	| expression '-' expression	{$$ = $1 - $3;}

	| expression '*' expression	{$$ = $1 * $3;}

	| expression '/' expression	{$$ = $3 ? $1 / $3 : 0;}

	| '-' expression    %prec UNARY {$$ = -$2;}

	| '+' expression    %prec UNARY {$$ = $2;}

	| '(' expression ')'		{$$ = $2;}

	| SIN '(' expression ')'	{$$ = sin ($3 * M_PI / 180);}

	| COS '(' expression ')'	{$$ = cos ($3 * M_PI / 180);}

	| TAN '(' expression ')'	{$$ = tan ($3 * M_PI / 180);}

	| SQRT '(' expression ')'	{$$ = sqrt ($3);}

	| DOUBLE			{$$ = $1;}

	| INTEGER			{$$ = $1;}
	;
