488 lines
9.9 KiB
C
488 lines
9.9 KiB
C
/**************************************************************************
|
||
GTA PROJECT AT division
|
||
Copyright, 1990, The Regents of the University of California.
|
||
Los Alamos National Laboratory
|
||
|
||
$Id$
|
||
DESCRIPTION: Phase 2 code generation routines for SNC.
|
||
Produces code and tables in C output file.
|
||
See also: gen_ss_code.c
|
||
ENVIRONMENT: UNIX
|
||
HISTORY:
|
||
19nov91,ajk Replaced lstLib calls with built-in linked list.
|
||
19nov91,ajk Removed extraneous "static" from "UserVar" declaration.
|
||
***************************************************************************/
|
||
|
||
#include <stdio.h>
|
||
#include "parse.h"
|
||
#include <dbDefs.h>
|
||
#include <seqU.h>
|
||
|
||
int num_channels = 0; /* number of db channels */
|
||
int num_ss = 0; /* number of state sets */
|
||
int num_errors = 0; /* number of errors detected in phase 2 processing */
|
||
|
||
/*+************************************************************************
|
||
* NAME: phase2
|
||
*
|
||
* CALLING SEQUENCE
|
||
* type argument I/O description
|
||
* ---------------------------------------------------
|
||
*
|
||
* RETURNS: n/a
|
||
*
|
||
* FUNCTION: Generate C code from tables.
|
||
*
|
||
* NOTES: All inputs are external globals.
|
||
*-*************************************************************************/
|
||
phase2()
|
||
{
|
||
extern Var *var_list; /* variables (from parse) */
|
||
extern Expr *ss_list; /* state sets (from parse) */
|
||
extern Expr *global_c_list; /* global C code */
|
||
|
||
/* Count number of db channels and state sets defined */
|
||
num_channels = db_chan_count();
|
||
num_ss = exprCount(ss_list);
|
||
|
||
/* Reconcile all variable and tie each to the appropriate VAR struct */
|
||
reconcile_variables();
|
||
|
||
/* reconcile all state names, including next state in transitions */
|
||
reconcile_states();
|
||
|
||
/* Generate preamble code */
|
||
gen_preamble();
|
||
|
||
/* Generate variable declarations */
|
||
gen_var_decl();
|
||
|
||
/* Generate definition C code */
|
||
gen_defn_c_code();
|
||
|
||
/* Assign bits for event flags */
|
||
assign_ef_bits();
|
||
|
||
/* Assign delay id's */
|
||
assign_delay_ids();
|
||
|
||
/* Generate code for each state set */
|
||
gen_ss_code();
|
||
|
||
/* Generate tables */
|
||
gen_tables();
|
||
|
||
/* Output global C code */
|
||
gen_global_c_code();
|
||
|
||
exit(0);
|
||
}
|
||
/* Generate preamble (includes, defines, etc.) */
|
||
gen_preamble()
|
||
{
|
||
extern char *prog_name;
|
||
extern int async_opt, conn_opt, debug_opt, reent_opt;
|
||
|
||
/* Program name (comment) */
|
||
printf("/* Program \"%s\" */\n", prog_name);
|
||
|
||
/* Include files */
|
||
printf("#include \"seq.h\"\n");
|
||
|
||
/* Local definitions */
|
||
printf("\n#define NUM_SS %d\n", num_ss);
|
||
printf("#define NUM_CHANNELS %d\n", num_channels);
|
||
|
||
/* #define's for compiler options */
|
||
gen_opt_defn(async_opt, "ASYNC_OPT");
|
||
gen_opt_defn(conn_opt, "CONN_OPT" );
|
||
gen_opt_defn(debug_opt, "DEBUG_OPT");
|
||
gen_opt_defn(reent_opt, "REENT_OPT");
|
||
printf("\n");
|
||
|
||
/* Forward references of tables: */
|
||
printf("\nextern SPROG %s;\n", prog_name);
|
||
printf("extern CHAN db_channels[];\n");
|
||
|
||
return;
|
||
}
|
||
|
||
gen_opt_defn(opt, defn_name)
|
||
int opt;
|
||
char *defn_name;
|
||
{
|
||
if (opt)
|
||
printf("#define %s TRUE\n", defn_name);
|
||
else
|
||
printf("#define %s FALSE\n", defn_name);
|
||
}
|
||
|
||
/* Reconcile all variables in an expression,
|
||
and tie each to the appropriate VAR struct */
|
||
int printTree = FALSE;
|
||
|
||
reconcile_variables()
|
||
{
|
||
extern Expr *ss_list, *exit_code_list;
|
||
Expr *ssp, *ep;
|
||
int connect_variable();
|
||
|
||
for (ssp = ss_list; ssp != 0; ssp = ssp->next)
|
||
{
|
||
#ifdef DEBUG
|
||
fprintf(stderr, "reconcile_variables: ss=%s\n", ssp->value);
|
||
#endif DEBUG
|
||
traverseExprTree(ssp, E_VAR, 0, connect_variable, 0);
|
||
}
|
||
|
||
/* Same for exit procedure */
|
||
for (ep = exit_code_list; ep != 0; ep = ep->next)
|
||
{
|
||
traverseExprTree(ep, E_VAR, 0, connect_variable, 0);
|
||
}
|
||
|
||
}
|
||
|
||
/* Connect a variable in an expression to the the Var structure */
|
||
int connect_variable(ep)
|
||
Expr *ep;
|
||
{
|
||
Var *vp;
|
||
extern char *stype[];
|
||
extern int warn_opt;
|
||
|
||
vp = (Var *)findVar(ep->value);
|
||
if (vp == 0)
|
||
{ /* variable not declared; add it to the variable list */
|
||
if (warn_opt)
|
||
fprintf(stderr,
|
||
"Warning: variable \"%s\" is used but not declared.\n",
|
||
ep->value);
|
||
vp = allocVar();
|
||
addVar(vp);
|
||
vp->name = ep->value;
|
||
vp->type = V_NONE; /* undeclared type */
|
||
vp->length = 0;
|
||
vp->value = 0;
|
||
}
|
||
ep->left = (Expr *)vp; /* make connection */
|
||
#ifdef DEBUG
|
||
fprintf(stderr, "connect_variable: %s\n", ep->value);
|
||
#endif
|
||
return;
|
||
}
|
||
|
||
/* Reconcile state names */
|
||
reconcile_states()
|
||
{
|
||
extern int num_errors;
|
||
Expr *ssp, *sp, *sp1, tr;
|
||
|
||
for (ssp = ss_list; ssp != 0; ssp = ssp->next)
|
||
{
|
||
for (sp = ssp->left; sp != 0; sp = sp->next)
|
||
{
|
||
/* Check for duplicate state names in this state set */
|
||
for (sp1 = sp->next; sp1 != 0; sp1 = sp1->next)
|
||
{
|
||
if (strcmp(sp->value, sp1->value) == 0)
|
||
{
|
||
fprintf(stderr,
|
||
"State \"%s\" is duplicated in state set \"%s\"\n",
|
||
sp->value, ssp->value);
|
||
num_errors++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Find a state by name */
|
||
Expr *find_state(name, sp)
|
||
char *name; /* state name */
|
||
Expr *sp; /* beginning of state list */
|
||
{
|
||
while (sp != 0)
|
||
{
|
||
if (strcmp(name, sp->value) == 0)
|
||
return sp;
|
||
sp = sp->next;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* Generate a C variable declaration for each variable declared in SNL */
|
||
gen_var_decl()
|
||
{
|
||
extern Var *var_list;
|
||
Var *vp;
|
||
char *vstr;
|
||
int nv;
|
||
extern int reent_opt;
|
||
|
||
printf("\n/* Variable declarations */\n");
|
||
|
||
/* Convert internal type to `C' type */
|
||
if (reent_opt)
|
||
printf("struct UserVar {\n");
|
||
for (nv=0, vp = var_list; vp != NULL; nv++, vp = vp->next)
|
||
{
|
||
switch (vp->type)
|
||
{
|
||
case V_CHAR:
|
||
vstr = "char";
|
||
break;
|
||
case V_INT:
|
||
case V_LONG:
|
||
vstr = "long";
|
||
break;
|
||
case V_SHORT:
|
||
vstr = "short";
|
||
break;
|
||
case V_FLOAT:
|
||
vstr = "float";
|
||
break;
|
||
case V_DOUBLE:
|
||
vstr = "double";
|
||
break;
|
||
case V_STRING:
|
||
vstr = "char";
|
||
break;
|
||
case V_EVFLAG:
|
||
case V_NONE:
|
||
vstr = NULL;
|
||
break;
|
||
default:
|
||
vstr = "int";
|
||
break;
|
||
}
|
||
|
||
if (vstr != NULL)
|
||
{
|
||
if (reent_opt)
|
||
printf("\t");
|
||
else
|
||
printf("static ");
|
||
printf("%s\t%s", vstr, vp->name);
|
||
if (vp->length > 0)
|
||
printf("[%d]", vp->length);
|
||
else if (vp->type == V_STRING)
|
||
printf("[MAX_STRING_SIZE]");
|
||
printf(";\n");
|
||
}
|
||
}
|
||
if (reent_opt)
|
||
printf("};\n");
|
||
return;
|
||
}
|
||
|
||
/* Generate definition C code (C code in definition section) */
|
||
gen_defn_c_code()
|
||
{
|
||
extern Expr *defn_c_list;
|
||
Expr *ep;
|
||
|
||
ep = defn_c_list;
|
||
if (ep != NULL)
|
||
{
|
||
printf("\n\t/* C code definitions */\n");
|
||
for (; ep != NULL; ep = ep->next)
|
||
{
|
||
print_line_num(ep->line_num, ep->src_file);
|
||
printf("%s\n", ep->left);
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
/* Generate global C code (C code following state program) */
|
||
gen_global_c_code()
|
||
{
|
||
extern Expr *global_c_list;
|
||
Expr *ep;
|
||
|
||
ep = global_c_list;
|
||
if (ep != NULL)
|
||
{
|
||
printf("\f\t/* Global C code */\n");
|
||
for (; ep != NULL; ep = ep->next)
|
||
{
|
||
print_line_num(ep->line_num, ep->src_file);
|
||
printf("%s\n", ep->left);
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
/* Returns number of db channels defined & inserts index into each channel struct */
|
||
db_chan_count()
|
||
{
|
||
extern Chan *chan_list;
|
||
int nchan;
|
||
Chan *cp;
|
||
|
||
nchan = 0;
|
||
for (cp = chan_list; cp != NULL; cp = cp->next)
|
||
{
|
||
if (cp->db_name != NULL)
|
||
{
|
||
cp->index = nchan;
|
||
nchan++;
|
||
#ifdef DEBUG
|
||
fprintf(stderr, "db_name=%s, index=%d\n",
|
||
cp->db_name, cp->index);
|
||
#endif
|
||
}
|
||
}
|
||
return nchan;
|
||
}
|
||
|
||
|
||
/* Assign bits to event flags and database variables */
|
||
assign_ef_bits()
|
||
{
|
||
extern Var *var_list;
|
||
Var *vp;
|
||
Chan *cp;
|
||
int ef_num;
|
||
extern int num_channels;
|
||
|
||
ef_num = num_channels + 1; /* start with 1-st avail mask bit */
|
||
printf("\n/* Event flags */\n");
|
||
#ifdef DEBUG
|
||
fprintf(stderr, "\nAssign values to event flags\n");
|
||
#endif
|
||
for (vp = var_list; vp != NULL; vp = vp->next)
|
||
{
|
||
cp = vp->chan;
|
||
/* First see if this is an event flag */
|
||
if (vp->type == V_EVFLAG)
|
||
{
|
||
if (cp != 0)
|
||
vp->ef_num = cp->index + 1; /* sync'ed */
|
||
else
|
||
vp->ef_num = ef_num++;
|
||
printf("#define %s\t%d\n", vp->name, vp->ef_num);
|
||
}
|
||
|
||
else
|
||
{
|
||
if (cp != 0)
|
||
vp->ef_num = cp->index + 1;
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
fprintf(stderr, "%s: ef_num=%d\n", vp->name, vp->ef_num);
|
||
#endif
|
||
}
|
||
return;
|
||
}
|
||
|
||
/* Assign a delay id to each "delay()" in an event (when()) expression */
|
||
assign_delay_ids()
|
||
{
|
||
extern Expr *ss_list;
|
||
Expr *ssp, *sp, *tp;
|
||
int delay_id;
|
||
int assign_next_delay_id();
|
||
|
||
for (ssp = ss_list; ssp != 0; ssp = ssp->next)
|
||
{
|
||
for (sp = ssp->left; sp != 0; sp = sp->next)
|
||
{
|
||
/* Each state has it's own delay id's */
|
||
delay_id = 0;
|
||
for (tp = sp->left; tp != 0; tp = tp->next)
|
||
{ /* traverse event expression only */
|
||
traverseExprTree(tp->left, E_FUNC, "delay",
|
||
assign_next_delay_id, &delay_id);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
assign_next_delay_id(ep, delay_id)
|
||
Expr *ep;
|
||
int *delay_id;
|
||
{
|
||
ep->right = (Expr *)*delay_id;
|
||
*delay_id += 1;
|
||
}
|
||
|
||
/* Traverse the expression tree, and call the supplied
|
||
* function on matched conditions */
|
||
traverseExprTree(ep, type, value, funcp, argp)
|
||
Expr *ep; /* ptr to start of expression */
|
||
int type; /* to search for */
|
||
char *value; /* with optional matching value */
|
||
void (*funcp)(); /* function to call */
|
||
void *argp; /* ptr to argument to pass on to function */
|
||
{
|
||
Expr *ep1;
|
||
extern char *stype[];
|
||
|
||
if (ep == 0)
|
||
return;
|
||
|
||
if (printTree)
|
||
fprintf(stderr, "traverseExprTree: type=%s, value=%s\n",
|
||
stype[ep->type], ep->value);
|
||
|
||
/* Call the function? */
|
||
if ((ep->type == type) &&
|
||
(value == 0 || strcmp(ep->value, value) == 0) )
|
||
{
|
||
funcp(ep, argp);
|
||
}
|
||
|
||
/* Continue traversing the expression tree */
|
||
switch(ep->type)
|
||
{
|
||
case E_EMPTY:
|
||
case E_VAR:
|
||
case E_CONST:
|
||
case E_STRING:
|
||
case E_TEXT:
|
||
case E_BREAK:
|
||
break;
|
||
|
||
case E_PAREN:
|
||
case E_UNOP:
|
||
case E_SS:
|
||
case E_STATE:
|
||
case E_FUNC:
|
||
case E_CMPND:
|
||
case E_STMT:
|
||
case E_ELSE:
|
||
case E_PRE:
|
||
case E_POST:
|
||
for (ep1 = ep->left; ep1 != 0; ep1 = ep1->next)
|
||
{
|
||
traverseExprTree(ep1, type, value, funcp, argp);
|
||
}
|
||
break;
|
||
|
||
case E_WHEN:
|
||
case E_ASGNOP:
|
||
case E_BINOP:
|
||
case E_SUBSCR:
|
||
case E_IF:
|
||
case E_WHILE:
|
||
case E_FOR:
|
||
case E_X:
|
||
for (ep1 = ep->left; ep1 != 0; ep1 = ep1->next)
|
||
{
|
||
traverseExprTree(ep1, type, value, funcp, argp);
|
||
}
|
||
for (ep1 = ep->right; ep1 != 0; ep1 = ep1->next)
|
||
{
|
||
traverseExprTree(ep1, type, value, funcp, argp);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
fprintf(stderr, "traverseExprTree: type=%d???\n", ep->type);
|
||
}
|
||
}
|
||
|