%{
/* SccsId = "@(#)spipar.y 4.9 (TU-Delft) 12/15/92" */
/**********************************************************

Name/Version      : sls_mkdb/4.9

Language          : C
Operating system  : UNIX SYSTEM V
Host machine      : HP9000

Author(s)         : A.J. van Genderen
Creation date     : 19-May-1992
Modified by       : 
Modification date :


        Delft University of Technology
        Department of Electrical Engineering
        Network Theory Section
        Mekelweg 4 - P.O.Box 5031
        2600 GA DELFT
        The Netherlands

        Phone : 015 - 786234

        COPYRIGHT (C) 1986 , All rights reserved
**********************************************************/
#include "sys_incl.h"
#class
#include "class.h"
#include "mkdbdefs.h"
#include "mkdbincl.h"

extern char yytext[];
extern Netelem * notconnected;

int resynch();
char *strsav();
double cvt_atof();
Stack *stackcpy();
Stack *xstackcpy();
Network *init_ntw();
Network *read_ntw();
Network *dev_tab[];

int n_net_cnt;
int n_inst_cnt = 0;
int n_term_cnt;

Stack 	*xs, *pxs;
Stack 	*attr_s;
Queue	*pq;
Queue	*netq;
Queue	*pnetq;
extern Dictionary *ntw_dict;
extern Dictionary *sym_dict;
extern Dictionary *dff_dict;

extern char **globNets;
extern int globNets_cnt;

Xelem 	*pxelem;
Netelem	*pnet;
Network *ntw;
Network *pntw;
NetReference *pnref;
NetworkInstance *inst;

char name_buf[BUFSIZ];
char net_buf[BUFSIZ];
char ntw_name[BUFSIZ];
char topcell[BUFSIZ];
char attr_buf[BUFSIZ];

int trunc_warn = 0;
int externspec = 0;
Network *curr_ntw;
int curr_inst_type;

char mc_type = 'i';  /* Indicates what type of model call is being parsed.
			Used for write to sim_mc stream. */

int behind_header = 0;   /* used for lexical analyzer to detect 
			    correct initial condition */

%}

%union {
	int   ival;
	char *sval;
	char cval;
	int *pival;
	float fval;
	double dval;
	}

%token <sval> INTEGER IDENTIFIER FLOAT
%token <sval> TOR_INST NETW_INST CAP_INST RES_INST
%token <sval> T_WIDTH T_LENGTH

%token SUBCKT ENDS END OTHER_CARD_BEGIN VNET RNET CNET
%token PERIOD ASSIGN LPS RPS OTHER
%token NEWLINE

%type <ival> ckt_term_list
%type <ival> two_con_list four_con_list connect_list connect
%type <ival> terminal_list terminal
%type <ival> i_val sub_ckt_head nom_ckt_head
%type <ival> net_stmt
%type <ival> x_inst_stmt x_inst_struct
%type <ival> t_inst_stmt t_inst_struct
%type <ival> r_inst_stmt r_inst_struct
%type <ival> c_inst_stmt c_inst_struct
%type <ival> o_inst_stmt o_inst_struct
%type <ival> attr_list single_attr
%type <dval> f_val attr_val real
%type <sval> i_attribute attr_spec
%type <sval> i_cell_name net_name net_ref ntw_name ident


%start spice_descr

%% 

spice_descr	: file_header sub_ckts nom_ckt
		;

file_header	: file_top ntw_name 
		{
		    behind_header = 1;

		    strcpy (topcell, $2);

		    /*
		    fprintf (stderr, "topcell %s\n", topcell);
		    */
		}
		  stuff eoc
		;

file_top	: /* empty */
		| file_top NEWLINE
		;

sub_ckts	: /* empty */
		| sub_ckts sub_ckt
		;

sub_ckt		: sub_ckt_head ckt_body sub_ckt_end
		{
			if(end_ntw($1, externspec, COMPLETE))
			{
			    sls_error(yylineno-1, sls_errno, NULL); 
			    die();
			}
		}
		;

nom_ckt		: nom_ckt_head ckt_body nom_ckt_end
		{
			if(end_ntw($1, externspec, COMPLETE))
			{
			    sls_error(yylineno-1, sls_errno, NULL); 
			    die();
			}
		}
		;

sub_ckt_head	: SUBCKT ntw_name
		{

			/*
			fprintf (stderr, "subckt %s\n", $2);
			*/

			if(ntw = (Network *)init_ntw($2, externspec)) {
			    curr_ntw = ntw;
			    $$ = (int) ntw;
			}
			else
			{
			    sls_errno = NTWAREF;
			    sls_error(yylineno, sls_errno, $2);
			    die();
			}
		}
		  ckt_term_list eoc
		{
			int i;

			$$ = (int)ntw;

			if ($4 && globNets_cnt > 0) {
			    pnetq = NULL;
			    for (i = 0; i < globNets_cnt; i++) {

				if (!(pnet = sym_dict->fetch(globNets[i]))) {

                                    /* add net 'globNets[i]' as an extra
				       terminal to the current network */

                                    if (!pnetq) {
					pnetq = new Queue (QueueType);
				    }

				    xs -> reset ();
				    pnet = new Netelem (globNets[i], 
							stackcpy((Stack *) xs),
							TermType);
				    pnet -> type = pnet -> type | N_GLOBADDED;
				    pnetq -> put ((Link *) pnet);
				    pnet = new Netelem (globNets[i], 
							stackcpy((Stack *) xs),
							TermType);
				    pnet -> type = pnet -> type | N_GLOBADDED;
				    sym_dict->store (pnet->name, (char *) pnet);
				    ntw -> netq -> put ((Link *) pnet);
				}
				else if (pnet -> xs) {
				    sls_errno = ILLRANGE;
				    sls_error (yylineno-1, sls_errno, 
							 globNets[i]);
				    $$ = 0;
				    break;
				}
			    }

			    if (pnetq) {
				if(ntw->termq)
				    ntw->termq->append((Queue *) pnetq);
				else
				    ntw -> termq = (Queue *) pnetq;
			    }
			}

			if ($$) {
			    if ($4)
				term_to_db((Queue *) $4);
			}
		}
		;

nom_ckt_head	: /* empty */
		{

			/*
			fprintf (stderr, "ckt %s\n", topcell);
			*/

			if(ntw = (Network *)init_ntw(topcell, externspec)) {
			    curr_ntw = ntw;
			    $$ = (int) ntw;
			}
			else
			{
			    sls_errno = NTWAREF;
			    sls_error(yylineno, sls_errno, topcell);
			    die();
			}
		}
		;

ckt_body	: stmt_list
		;

sub_ckt_end	: ENDS stuff eoc
		;

nom_ckt_end	: END stuff eoc
		{
		    behind_header = 0;
		}
		;

ckt_term_list	: terminal_list 
		{
			if($1)
			{
			    if(ntw->termq)
				ntw->termq->append((Queue *) $1);
			    else
			    	ntw -> termq = (Queue *) $1;
			    $$ = (int) $1;
			}
			else {
			    $$ = 0;
			}
		}
		;

terminal_list	: terminal
		{
			if ($1) {
			    pnetq = new Queue (QueueType);
			    pnetq->put((Link *) $1);
			    $$ = (int) pnetq;
			}
                        else
			    $$ = 0;
		}
		| terminal_list terminal
		{
			if ($1 && $2)
			    ((Queue *) $1)->put((Link *) $2);
			$$ = $1;
		}
		;

terminal	: net_name
		{
			xs->reset();

			if(sym_dict->fetch($1))
			{
			    sls_errno = TERMADEF;
			    sls_error(yylineno, sls_errno, $1);
			    $$ = 0;
			}
			else
			{
			    pnet = new Netelem ($1, stackcpy((Stack *) xs),
				       		TermType);
			    $$ = (int) pnet;
			    pnet = new Netelem ($1, stackcpy((Stack *) xs),
						TermType);
			    sym_dict->store(pnet->name, (char *) pnet);
			    ntw -> netq -> put ((Link *) pnet);
			}
		}
		;

stmt_list	: /* empty */
		| stmt_list statement
		;

statement	: inst_stmt eoc
		{
			if(inst && curr_inst_type)
			{
			    if(ntw->mcq == NULL)
				ntw -> mcq = new Queue (QueueType);
			    
			    ntw -> mcq -> put ((Link *) inst);
			    inst_to_db((NetworkInstance *) inst);
			}
		}
		| net_stmt eoc
		| other_card eoc
		| error
		{
		    	if(resynch(';'))
			{
		    	    yyerrok;
		    	    yyclearin;
			}
			else
			    die();
		}
		;

net_stmt	: VNET two_con_list real
		{
		    if ($3 < 1.0e-9 && $3 > -1.0e-9) {
			if (neteqv((Queue *) $2)) {
			    sls_error(yylineno, sls_errno, NULL);
			}
		    }
		}
		| RNET two_con_list real
		{
		    if ($3 < 1.1e-3 && $3 > -1.1e-3) {
			if (neteqv((Queue *) $2)) {
			    sls_error(yylineno, sls_errno, NULL);
			}
		    }
		    else if (!noWarnings) {
			fprintf (stderr, "Warning: card 'rnetxxx' skipped\n");
		    }
		}
		| CNET two_con_list
		{
		    if (neteqv((Queue *) $2)) {
			sls_error(yylineno, sls_errno, NULL);
		    }
		}
		;

inst_stmt	: x_inst_stmt
		| t_inst_stmt
		| r_inst_stmt
		| c_inst_stmt
		| o_inst_stmt
		;

other_card	: OTHER_CARD_BEGIN stuff
		;

x_inst_stmt	: x_inst_struct connect_list ident attr_list
		{
		    int net_cnt;
		    int inst_cnt;
		    int dev;
		    Stack * inst_xs;

		    /*
		    fprintf (stderr, "instance %s %s\n", 
			     ((InstanceStruct *)$1)->inst_name, $3);
		    */

		    if ($1 && $2) {
			if ($3 == NULL) 
			    yyerror (NULL);
			if ((pntw = (Network *) ntw_dict -> fetch($3)) 
								   == NULL)
			{
			    if ((pntw = read_ntw ($3)) == NULL)
			    {
				sls_errno = UNKNETWORK;
				sls_error(yylineno-1, sls_errno, $3);
				die();
			    }
			}

			$$ = (int) new NetworkInstance(pntw, NULL);

			((Link *)$$) -> type = curr_inst_type;

			inst = (NetworkInstance *) $$;

			inst->inst_struct = (InstanceStruct *) $1;
			sym_dict->store(inst->inst_struct->inst_name,
					(char *) inst);

                        /* set n_*_cnt for possibly adding extra global nets 
			   in match_net() */

			inst_xs = inst -> inst_struct -> inst_construct;
			if(inst_xs)
			    n_inst_cnt = getxslength (inst_xs);
			else
			    n_inst_cnt = 1;
			n_net_cnt = getnetcnt((Queue *) $2);
			n_term_cnt = gettermcnt (inst -> ntw);
			if (n_net_cnt % n_inst_cnt != 0)
			    n_inst_cnt = 0;

			inst -> ntw_attr = stackcpy(attr_s);
			if(inst_net_eqv(inst, (Queue *) $2))
			{
			    sls_error(yylineno-1, sls_errno, NULL);
			}
			n_inst_cnt = 0;
		    }
		}
		;

t_inst_stmt	: t_inst_struct four_con_list ident attr_list
		{
		    int net_cnt;
		    int inst_cnt;
		    int dev;
		    Stack * inst_xs;

		    /*
		    fprintf (stderr, "instance %s %s\n", 
			     ((InstanceStruct *)$1)->inst_name, $3);
		    */

		    if ($1 && $2) {
			if ($3 == NULL) 
			    yyerror (NULL);
			if ((pntw = read_ntw ($3)) == NULL) {

			    /* if the transistor is not defined in the database,
			       it must be a predefined transistor.
			    */

			    if (strcmp ($3, "nenh") == 0) dev = DevNenh;
			    else if (strcmp ($3, "penh") == 0) dev = DevPenh;
			    else if (strcmp ($3, "ndep") == 0) dev = DevNdep;
			    else {
				sls_errno = UNKDEVICE;
				sls_error(yylineno-1, sls_errno, $3);
				die();
			    }
			    pntw = dev_tab[dev];
			}
			$$ = (int) new NetworkInstance(pntw, NULL);

			((Link *)$$) -> type = curr_inst_type;

			inst = (NetworkInstance *) $$;

			inst->inst_struct = (InstanceStruct *) $1;
			sym_dict->store(inst->inst_struct->inst_name,
					(char *) inst);

			n_inst_cnt = 0;

			inst -> ntw_attr = stackcpy(attr_s);
			if(inst_net_eqv(inst, (Queue *) $2))
			{
			    sls_error(yylineno-1, sls_errno, NULL);
			}
			n_inst_cnt = 0;
		    }
		}
		;

r_inst_stmt	: r_inst_struct two_con_list i_cell_name single_attr
		{
		    int net_cnt;
		    int inst_cnt;
		    int dev;
		    Stack * inst_xs;

		    /*
		    fprintf (stderr, "instance %s %s\n", 
			     ((InstanceStruct *)$1)->inst_name, $3);
		    */

		    if ($1 && $2) {
			$$ = (int) new NetworkInstance(dev_tab[DevRes], NULL);

			((Link *)$$) -> type = curr_inst_type;

			inst = (NetworkInstance *) $$;

			inst->inst_struct = (InstanceStruct *) $1;
			sym_dict->store(inst->inst_struct->inst_name,
					(char *) inst);

			n_inst_cnt = 0;

			inst -> ntw_attr = stackcpy(attr_s);
			if(inst_net_eqv(inst, (Queue *) $2))
			{
			    sls_error(yylineno-1, sls_errno, NULL);
			}
			n_inst_cnt = 0;
		    }
		}
		;

c_inst_stmt	: c_inst_struct two_con_list i_cell_name single_attr
		{
		    int net_cnt;
		    int inst_cnt;
		    int dev;
		    Stack * inst_xs;

		    /*
		    fprintf (stderr, "instance %s %s\n", 
			     ((InstanceStruct *)$1)->inst_name, $3);
		    */

		    if ($1 && $2) {
			$$ = (int) new NetworkInstance(dev_tab[DevCap], NULL);

			((Link *)$$) -> type = curr_inst_type;

			inst = (NetworkInstance *) $$;

			inst->inst_struct = (InstanceStruct *) $1;
			sym_dict->store(inst->inst_struct->inst_name,
					(char *) inst);

			n_inst_cnt = 0;

			inst -> ntw_attr = stackcpy(attr_s);
			if(inst_net_eqv(inst, (Queue *) $2))
			{
			    sls_error(yylineno-1, sls_errno, NULL);
			}
			n_inst_cnt = 0;
		    }
		}
		;

o_inst_stmt	: o_inst_struct stuff
		;

x_inst_struct	: NETW_INST
                {
			curr_inst_type = NetworkType;

			xs->reset();

                        if(sym_dict->fetch(yytext))
                        {
                            sls_errno = INSTADEF;
                            sls_error(yylineno, sls_errno, yytext);
                            $$ = 0;
                        }
                        else
                        {
                            $$ = (int) new InstanceStruct (strsav(yytext),
                                           stackcpy((Stack *) xs));
                        }
                }
                ;

t_inst_struct	: TOR_INST
                {
			curr_inst_type = TransistorType;

			xs->reset();

                        if(sym_dict->fetch(yytext))
                        {
                            sls_errno = INSTADEF;
                            sls_error(yylineno, sls_errno, yytext);
                            $$ = 0;
                        }
                        else
                        {
                            $$ = (int) new InstanceStruct (strsav(yytext),
                                           stackcpy((Stack *) xs));
                        }
                }
                ;

r_inst_struct	: RES_INST
                {
			curr_inst_type = ResistorType;

			xs->reset();

                        if(sym_dict->fetch(yytext))
                        {
                            sls_errno = INSTADEF;
                            sls_error(yylineno, sls_errno, yytext);
                            $$ = 0;
                        }
                        else
                        {
                            $$ = (int) new InstanceStruct (strsav(yytext),
                                           stackcpy((Stack *) xs));
                        }
                }
                ;

c_inst_struct	: CAP_INST
                {
			curr_inst_type = CapacitorType;

			xs->reset();

                        if(sym_dict->fetch(yytext))
                        {
                            sls_errno = INSTADEF;
                            sls_error(yylineno, sls_errno, yytext);
                            $$ = 0;
                        }
                        else
                        {
                            $$ = (int) new InstanceStruct (strsav(yytext),
                                           stackcpy((Stack *) xs));
                        }
                }
                ;

o_inst_struct	: ident
		{
			curr_inst_type = 0;
			$$ = 0;
		}
		;

attr_list	: /* empty */
		{
			attr_s->reset();
			$$ = (int) attr_s;
		}
		| attr_list i_attribute
		{
			((Stack *) $1) -> push((char *) strsav($2) );
			$$ = $1;
		}
		;

single_attr	: real
		{
			attr_s->reset();
		    	sprintf(attr_buf, "v=%g", $1);
			attr_s -> push(strsav(attr_buf));

			$$ = (int) attr_s;
		}
		;

i_attribute	: attr_spec ASSIGN attr_val
		{
		    	sprintf(attr_buf, "%s=%g", $1, $3);
			$$ = attr_buf;
		}
		;

attr_spec	: T_WIDTH
		{
			$$ = "w";
		}
		| T_LENGTH
		{
			$$ = "l";
		}
		;

attr_val	: real
		{
			$$ = $1;
		}
		;

real		: i_val
		{
			$$ = (double) $1;
		}
		| f_val
		{
			$$ = $1;
		}
		;

f_val		: FLOAT
		{
			$$ = cvt_atof($1);
		}
		;

two_con_list	: connect connect
		{
		    pnetq = new Queue (QueueType);

		    if($1) {
			pnetq->put((Link *) $1);
		    }

		    if($2) {
			pnetq->put((Link *) $2);
		    }

		    pnetq -> type = PmQueue;

		    $$ = (int) pnetq;
		}
		;

four_con_list	: connect connect connect connect
		{
		    pnetq = new Queue (QueueType);

		    if($1) {
			pnetq->put((Link *) $1);
		    }

		    if($2) {
			pnetq->put((Link *) $2);
		    }

		    if($3) {
			pnetq->put((Link *) $3);
		    }

		    if($4) {
			pnetq->put((Link *) $4);
		    }

		    pnetq -> type = PmQueue;

		    $$ = (int) pnetq;
		}
		;

connect_list	: connect
		{
		    pnetq = new Queue (QueueType);

		    if($1)
		    {
			pnetq->put((Link *) $1);
		    }

		    $$ = (int) pnetq;

		    pnetq -> type = PmQueue;
		}
		| connect_list connect
		{
		    if($2)
		    {
			((Queue *) $1)->put((Link *) $2);
		    }

		    $$ = $1;
		}
		;

connect		: net_ref
		{
			/* If net yet not defined, a new net will be defined
			   and stored in the symbol table and put in the
			   network net queue. Additionally a net reference
			   is defined and returned by this action	    */

			/*
			fprintf (stderr, "net_ref: %s\n", $1);
			*/

			if((pnet = sym_dict->fetch($1)) == NULL)
			{
			    pnet = new Netelem ($1, NULL, NetType);
			    sym_dict->store(pnet->name, (char *) pnet);
			    ntw -> netq -> put((Link*) pnet);
			}
			$$ = (int) new NetReference (pnet,NULL);
		}
		;

net_ref		: INTEGER
		{
			strcpy (net_buf, yytext);
			$$ = net_buf;
		}
		| ident
		{
			strcpy (net_buf, $1);
			$$ = net_buf;
		}
		;

i_cell_name	: /* empty */
		{
		    $$ = 0;
		}
		| ident
		{
		    $$ = $1;
		}
		;
		
ntw_name	: ident
		{
			strcpy (ntw_name, $1);
			$$ = ntw_name;
		}
		;

net_name	: INTEGER
		{
			strcpy (net_buf, yytext);
			$$ = net_buf;
		}
		| ident
		{
			strcpy (net_buf, $1);
			$$ = net_buf;
		}
		;

stuff		: /* empty */
		| stuff stuff_item
		;

stuff_item	: INTEGER
		| IDENTIFIER 
		| FLOAT
		| SUBCKT 
		| ENDS 
		| END 
		| OTHER_CARD_BEGIN
		| PERIOD 
		| ASSIGN 
		| LPS 
		| RPS 
		| T_WIDTH 
		| T_LENGTH
		| OTHER
		;

ident		: IDENTIFIER
		{
			strcpy (name_buf, $1);

			if (dmTestname (name_buf) == 1) {
			    if (!trunc_warn && !noWarnings) {
			        fprintf (stderr, 
			        "Warning: name(s) truncated to %d characters\n",
			        DM_MAXNAME);
			        trunc_warn = 1;
			    }
			}
			$$ = name_buf;
		}
		;

i_val		: INTEGER
		{
		    $$ = atoi($1);
		}
		;

eoc		: NEWLINE
		| eoc NEWLINE
		;


%%

void yyerror(s)
char * s;
{
    sls_error(yylineno,SYNTAX,NULL);
}

int resynch(c)
char c;
{
    int c1,
	newline = 0;

    fprintf(stderr," \"");
    if(yytext)
    	fprintf(stderr,"%s",yytext);
    while((c1 = Input()) != c && c1 != 0)
    {
	if(!(newline = (newline || ((c1=='\n')?1:0))))
        fprintf(stderr,"%c",c1);
    }

    fprintf(stderr,"\"\n");

    return(c1);
}

void stackfree(xs, type)
Stack *xs;
int type;
{
    char *p;

    if(xs)
    {
         while(! xs->empty())
         {
	     p = xs -> pop();
	     switch (type) {
	         case XELEM:
		     if(p)
		         delete (Xelem *) p;
		     break;
	         case STRING:
		     if(p)
		     {
#ifdef DMEM
    char_nbyte -=  strlen(p) + 1;
#endif
		         delete p;
		     }
		     break;
	     }
         }
    }
}

Netelem *findterm(inst, term_name)
NetworkInstance *inst;
char *term_name;
{
    Queue *termq;
    int termq_len;
    Netelem *pterm;
    int i;

    if (inst)
	termq = inst -> ntw -> termq;
    else
	termq = ntw -> termq;

    termq_len = termq -> length ();

    for(i=0, pterm = (Netelem *) termq -> first_elem ();
	i < termq_len;
	i++, pterm = (Netelem *) termq -> next_elem ((Link *) pterm))
    {
	if(strcmp(pterm->name, term_name) == 0)
	    return(pterm);
    }

    return(0);
}

int gettermcnt (netw)
Network *netw;
{
    Queue *termq;
    int termq_len;
    Netelem *pterm;
    int i;
    int term_cnt;

    termq = netw -> termq;
    termq_len = termq -> length ();
    term_cnt = 0;

    for(i=0, pterm = (Netelem *) termq -> first_elem ();
	i < termq_len;
	i++, pterm = (Netelem *) termq -> next_elem ((Link *) pterm))
    {
	term_cnt += getxslength (pterm -> xs);
    }

    return (term_cnt);
}

void prxs(xs)
Stack *xs;
{

    int i;
    int xs_len = xs -> length();

    fprintf(stderr,"stack: %x, length: %d\n", xs, xs_len);
    if(xs)
    {
        for(i=0; i<xs_len; i++)
        {
	    pxelem = (Xelem *) xs -> access(i);
	    fprintf(stderr,"lb: %d, rb: %d\n", pxelem -> left_bound,
					       pxelem -> right_bound);
        }
    }
}
