/************************************************************************** GTA PROJECT AT division Copyright, 1990, The Regents of the University of California. Los Alamos National Laboratory gen_ss_code.c,v 1.2 1995/06/27 15:25:43 wright Exp DESCRIPTION: gen_ss_code.c -- routines to generate state set code ENVIRONMENT: UNIX HISTORY: 19nov91,ajk Changed find_var() to findVar(). 28apr92,ajk Implemented efClear() & efTestAndClear(). 01mar94,ajk Changed table generation to the new structures defined in seqCom.h. ***************************************************************************/ #include #include "parse.h" #include "cadef.h" #include #include /*+************************************************************************ * NAME: gen_ss_code * * CALLING SEQUENCE * type argument I/O description * --------------------------------------------------- * * RETURNS: * * FUNCTION: Generate state set C code from tables. * * NOTES: All inputs are external globals. *-*************************************************************************/ /*#define DEBUG 1*/ #define EVENT_STMT 1 #define ACTION_STMT 2 #define DELAY_STMT 3 #define EXIT_STMT 4 gen_ss_code() { extern Expr *ss_list; Expr *ssp; Expr *sp; /* For each state set ... */ for (ssp = ss_list; ssp != NULL; ssp = ssp->next) { /* For each state ... */ for (sp = ssp->left; sp != NULL; sp = sp->next) { printf("\f/* Code for state \"%s\" in state set \"%s\" */\n", sp->value, ssp->value); /* Generate function to set up for delay processing */ gen_delay_func(sp, ssp); /* Generate event processing function */ gen_event_func(sp, ssp); /* Generate action processing function */ gen_action_func(sp, ssp); } } /* Generate exit handler code */ gen_exit_handler(); } /* Generate a function for each state that sets up delay processing: * This function gets called prior to the event function to guarantee * that the initial delay value specified in delay() calls are used. * Each delay() call is assigned a unique id. The maximum number of * delays is recorded in the state set structure. */ gen_delay_func(sp, ssp) Expr *ssp; Expr *sp; { Expr *tp; int eval_delay(); printf("\n/* Delay function for state \"%s\" in state set \"%s\" */\n", sp->value, ssp->value); printf("static void D_%s_%s(ssId, pVar)\n", ssp->value, sp->value); printf("SS_ID\tssId;\n"); printf("struct UserVar\t*pVar;\n{\n"); /* For each transition: */ for (tp = sp->left; tp != NULL; tp = tp->next) { print_line_num(tp->line_num, tp->src_file); traverseExprTree(tp, E_FUNC, "delay", eval_delay, sp); } printf("}\n"); } /* Evaluate the expression within a delay() function and generate * a call to seq_delayInit(). Adds ssId, delay id parameters and cast to float. * Example: seq_delayInit(ssId, 1, (float)()); */ eval_delay(ep, sp) Expr *ep; Expr *sp; { Expr *epf; int delay_id; extern char *stype[]; #ifdef DEBUG fprintf(stderr, "eval_delay: type=%s\n", stype[ep->type]); #endif /*DEBUG*/ /* Generate 1-st part of function w/ 1-st 2 parameters */ delay_id = (int)ep->right; /* delay id was previously assigned */ printf("\tseq_delayInit(ssId, %d, (", delay_id); /* Evaluate & generate the 3-rd parameter (an expression) */ eval_expr(EVENT_STMT, ep->left, sp, 0); /* Complete the function call */ printf("));\n"); } /* Generate action processing functions: Each state has one action routine. It's name is derived from the state set name and the state name. */ gen_action_func(sp, ssp) Expr *sp; Expr *ssp; /* Parent state set */ { Expr *tp; Expr *ap; int trans_num; extern char *prog_name; extern line_num; /* Action function declaration */ printf("\n/* Action function for state \"%s\" in state set \"%s\" */\n", sp->value, ssp->value); printf("static void A_%s_%s(ssId, pVar, transNum)\n", ssp->value, sp->value); printf("SS_ID\tssId;\n"); printf("struct UserVar\t*pVar;\n"); printf("short\ttransNum;\n{\n"); /* "switch" statment based on the transition number */ printf("\tswitch(transNum)\n\t{\n"); trans_num = 0; line_num = 0; /* For each transition ("when" statement) ... */ for (tp = sp->left; tp != NULL; tp = tp->next) { /* "case" for each transition */ printf("\tcase %d:\n", trans_num); /* For each action statement insert action code */ for (ap = tp->right; ap != NULL; ap = ap->next) { if (line_num != ap->line_num) { print_line_num(ap->line_num, ap->src_file); line_num = ap->line_num; } /* Evaluate statements */ eval_expr(ACTION_STMT, ap, sp, 2); } /* end of case */ printf("\t\treturn;\n"); trans_num++; } /* end of switch stmt */ printf("\t}\n"); /* end of function */ printf("}\n"); return; } /* Generate a C function that checks events for a particular state */ gen_event_func(sp, ssp) Expr *sp; Expr *ssp; { Expr *tp; int index, trans_num; printf("\n/* Event function for state \"%s\" in state set \"%s\" */\n", sp->value, ssp->value); printf("static long E_%s_%s(ssId, pVar, pTransNum, pNextState)\n", ssp->value, sp->value); printf("SS_ID\tssId;\n"); printf("struct UserVar\t*pVar;\n"); printf("short\t*pTransNum, *pNextState;\n{\n"); trans_num = 0; /* For each transition generate an "if" statement ... */ for (tp = sp->left; tp != NULL; tp = tp->next) { print_line_num(tp->line_num, tp->src_file); printf("\tif ("); if (tp->left == 0) printf("TRUE"); else eval_expr(EVENT_STMT, tp->left, sp, 0); printf(")\n\t{\n"); /* index is the transition number (0, 1, ...) */ index = state_block_index_from_name(ssp, tp->value); if (index < 0) { fprintf(stderr, "Line %d: ", tp->line_num); fprintf(stderr, "No state %s in state set %s\n", tp->value, ssp->value); index = 0; /* default to 1-st state */ printf("\t\t/* state %s does not exist */\n", tp->value); } printf("\t\t*pNextState = %d;\n", index); printf("\t\t*pTransNum = %d;\n", trans_num); printf("\t\treturn TRUE;\n\t}\n"); trans_num++; } printf("\treturn FALSE;\n"); printf("}\n"); } /* Given a state name and state set struct, find the corresponding state struct and return its index (1-st one is 0) */ state_block_index_from_name(ssp, state_name) Expr *ssp; char *state_name; { Expr *sp; int index; index = 0; for (sp = ssp->left; sp != NULL; sp = sp->next) { if (strcmp(state_name, sp->value) == 0) return index; index++; } return -1; /* State name non-existant */ } /* Evaluate an expression. */ eval_expr(stmt_type, ep, sp, level) int stmt_type; /* EVENT_STMT, ACTION_STMT, or DELAY_STMT */ Expr *ep; /* ptr to expression */ Expr *sp; /* ptr to current State struct */ int level; /* indentation level */ { Expr *epf; int nparams; extern int reent_opt; extern int line_num; if (ep == 0) return; switch(ep->type) { case E_CMPND: indent(level); printf("{\n"); line_num += 1; for (epf = ep->left; epf != 0; epf = epf->next) { eval_expr(stmt_type, epf, sp, level+1); } indent(level); printf("}\n"); line_num += 1; break; case E_STMT: indent(level); eval_expr(stmt_type, ep->left, sp, 0); printf(";\n"); line_num += 1; break; case E_IF: case E_WHILE: indent(level); if (ep->type == E_IF) printf("if ("); else printf("while ("); eval_expr(stmt_type, ep->left, sp, 0); printf(")\n"); line_num += 1; epf = ep->right; if (epf->type == E_CMPND) eval_expr(stmt_type, ep->right, sp, level); else eval_expr(stmt_type, ep->right, sp, level+1); break; case E_FOR: indent(level); printf("for ("); eval_expr(stmt_type, ep->left->left, sp, 0); printf("; "); eval_expr(stmt_type, ep->left->right, sp, 0); printf("; "); eval_expr(stmt_type, ep->right->left, sp, 0); printf(")\n"); line_num += 1; epf = ep->right->right; if (epf->type == E_CMPND) eval_expr(stmt_type, epf, sp, level); else eval_expr(stmt_type, epf, sp, level+1); break; case E_ELSE: indent(level); printf("else\n"); line_num += 1; epf = ep->left; /* Is it "else if" ? */ if (epf->type == E_IF || epf->type == E_CMPND) eval_expr(stmt_type, ep->left, sp, level); else eval_expr(stmt_type, ep->left, sp, level+1); break; case E_VAR: #ifdef DEBUG fprintf(stderr, "E_VAR: %s\n", ep->value); #endif /*DEBUG*/ if(reent_opt) { /* Make variables point to allocated structure */ Var *vp; vp = (Var *)ep->left; if (vp->type != V_NONE && vp->type != V_EVFLAG) printf("(pVar->%s)", ep->value); else printf("%s", ep->value); } else printf("%s", ep->value); break; case E_CONST: printf("%s", ep->value); break; case E_STRING: printf("\"%s\"", ep->value); break; case E_BREAK: indent(level); printf("break;\n"); line_num += 1; break; case E_FUNC: #ifdef DEBUG fprintf(stderr, "E_FUNC: %s\n", ep->value); #endif /*DEBUG*/ if (special_func(stmt_type, ep, sp)) break; printf("%s(", ep->value); for (epf = ep->left, nparams = 0; epf != 0; epf = epf->next, nparams++) { if (nparams > 0) printf(" ,"); eval_expr(stmt_type, epf, sp, 0); } printf(") "); break; case E_ASGNOP: case E_BINOP: eval_expr(stmt_type, ep->left, sp, 0); printf(" %s ", ep->value); eval_expr(stmt_type, ep->right, sp, 0); break; case E_PAREN: printf("("); eval_expr(stmt_type, ep->left, sp, 0); printf(")"); break; case E_UNOP: printf("%s", ep->value); eval_expr(stmt_type, ep->left, sp, 0); break; case E_PRE: printf("%s", ep->value); eval_expr(stmt_type, ep->left, sp, 0); break; case E_POST: eval_expr(stmt_type, ep->left, sp, 0); printf("%s", ep->value); break; case E_SUBSCR: eval_expr(stmt_type, ep->left, sp, 0); printf("["); eval_expr(stmt_type, ep->right, sp, 0); printf("]"); break; case E_TEXT: printf("%s\n", ep->left); line_num += 1; break; default: if (stmt_type == EVENT_STMT) printf("TRUE"); /* empty event statement defaults to TRUE */ else printf(" "); break; } } indent(level) int level; { while (level-- > 0) printf("\t"); } /* func_name_to_code - convert function name to a code */ enum fcode { F_DELAY, F_EFSET, F_EFTEST, F_EFCLEAR, F_EFTESTANDCLEAR, F_PVGET, F_PVPUT, F_PVTIMESTAMP, F_PVASSIGN, F_PVMONITOR, F_PVSTOPMONITOR, F_PVCOUNT, F_PVINDEX, F_PVSTATUS, F_PVSEVERITY, F_PVFLUSH, F_PVERROR, F_PVGETCOMPLETE, F_PVASSIGNED, F_PVCONNECTED, F_PVCHANNELCOUNT, F_PVCONNECTCOUNT, F_PVASSIGNCOUNT, F_PVDISCONNECT, F_SEQLOG, F_MACVALUEGET, F_OPTGET, F_NONE }; char *fcode_str[] = { "delay", "efSet", "efTest", "efClear", "efTestAndClear", "pvGet", "pvPut", "pvTimeStamp", "pvAssign", "pvMonitor", "pvStopMonitor", "pvCount", "pvIndex", "pvStatus", "pvSeverity", "pvFlush", "pvError", "pvGetComplete", "pvAssigned", "pvConnected", "pvChannelCount", "pvConnectCount", "pvAssignCount", "pvDisconnect", "seqLog", "macValueGet", "optGet", NULL }; enum fcode func_name_to_code(fname) char *fname; { int i; for (i = 0; fcode_str[i] != NULL; i++) { if (strcmp(fname, fcode_str[i]) == 0) return (enum fcode)i; } return F_NONE; } /* Process special function (returns TRUE if this is a special function) Checks for one of the following special functions: - event flag functions, e.g. pvSet() - process variable functions, e.g. pvPut() - delay() - macVauleget() - seqLog() */ special_func(stmt_type, ep, sp) int stmt_type; /* ACTION_STMT or EVENT_STMT */ Expr *ep; /* ptr to function in the expression */ Expr *sp; /* current State struct */ { char *fname; /* function name */ Expr *ep1, *ep2, *ep3; /* parameters */ Chan *cp; Var *vp; enum fcode func_code; int delay_id; fname = ep->value; func_code = func_name_to_code(fname); if (func_code == F_NONE) return FALSE; /* not a special function */ #ifdef DEBUG fprintf(stderr, "special_func: func_code=%d\n", func_code); #endif /*DEBUG*/ switch (func_code) { case F_DELAY: delay_id = (int)ep->right; printf("seq_delay(ssId, %d)", delay_id); return TRUE; case F_EFSET: case F_EFTEST: case F_EFCLEAR: case F_EFTESTANDCLEAR: /* Event flag funtions */ gen_ef_func(stmt_type, ep, sp, fname); return TRUE; case F_PVPUT: case F_PVGET: case F_PVTIMESTAMP: case F_PVGETCOMPLETE: case F_PVSTATUS: case F_PVSEVERITY: case F_PVCONNECTED: case F_PVASSIGNED: case F_PVMONITOR: case F_PVSTOPMONITOR: case F_PVCOUNT: case F_PVINDEX: case F_PVDISCONNECT: case F_PVASSIGN: /* DB functions requiring a channel id */ gen_pv_func(stmt_type, ep, sp, fname, func_code); return TRUE; case F_PVFLUSH: case F_PVERROR: case F_PVCHANNELCOUNT: case F_PVCONNECTCOUNT: case F_PVASSIGNCOUNT: /* DB functions NOT requiring a channel structure */ printf("seq_%s(ssId)", fname); return TRUE; case F_SEQLOG: case F_MACVALUEGET: case F_OPTGET: /* Any funtion that requires adding ssID as 1st parameter. * Note: name is changed by prepending "seq_". */ printf("seq_%s(ssId", fname); /* now fill in user-supplied paramters */ for (ep1 = ep->left; ep1 != 0; ep1 = ep1->next) { printf(", "); eval_expr(stmt_type, ep1, sp, 0); } printf(") "); return TRUE; default: /* Not a special function */ return FALSE; } } /* Generate code for all event flag functions */ gen_ef_func(stmt_type, ep, sp, fname, func_code) int stmt_type; /* ACTION_STMT or EVENT_STMT */ Expr *ep; /* ptr to function in the expression */ Expr *sp; /* current State struct */ char *fname; /* function name */ enum fcode func_code; { Expr *ep1, *ep2, *ep3; Var *vp; Chan *cp; ep1 = ep->left; /* ptr to 1-st parameters */ if ( (ep1 != 0) && (ep1->type == E_VAR) ) vp = (Var *)findVar(ep1->value); else vp = 0; if (vp == 0 || vp->type != V_EVFLAG) { fprintf(stderr, "Line %d: ", ep->line_num); fprintf(stderr, "Parameter to \"%s\" must be an event flag\n", fname); } else if (func_code == F_EFSET && stmt_type == EVENT_STMT) { fprintf(stderr, "Line %d: ", ep->line_num); fprintf(stderr, "efSet() cannot be used as an event.\n"); } else { printf("seq_%s(ssId, %s)", fname, vp->name); } return; } /* Generate code for pv functions requiring a database variable. * The channel id (index into channel array) is substituted for the variable */ gen_pv_func(stmt_type, ep, sp, fname) int stmt_type; /* ACTION_STMT or EVENT_STMT */ Expr *ep; /* ptr to function in the expression */ Expr *sp; /* current State struct */ char *fname; /* function name */ { Expr *ep1, *ep2, *ep3; Var *vp; Chan *cp; int index; ep1 = ep->left; /* ptr to 1-st parameter in the function */ if (ep1 == 0) { fprintf(stderr, "Line %d: ", ep->line_num); fprintf(stderr, "Function \"%s\" requires a parameter.\n", fname); return; } vp = 0; if (ep1->type == E_VAR) { vp = (Var *)findVar(ep1->value); } else if (ep1->type == E_SUBSCR) { /* Form should be: [] */ ep2 = ep1->left; /* variable */ ep3 = ep1->right; /* subscript */ if ( ep2->type == E_VAR ) { vp = (Var *)findVar(ep2->value); } } if (vp == 0) { fprintf(stderr, "Line %d: ", ep->line_num); fprintf(stderr, "Parameter to \"%s\" is not a defined variable.\n", fname); cp=0; } else { #ifdef DEBUG fprintf(stderr, "gen_pv_func: var=%s\n", ep1->value); #endif /*DEBUG*/ cp = vp->chan; index = cp->index; } if ( (vp != 0) && (cp == 0) ) { fprintf(stderr, "Line %d: ", ep->line_num); fprintf(stderr, "Parameter to \"%s\" must be DB variable.\n", fname); index=-1; } printf("seq_%s(ssId, %d", fname, index); if (ep1->type == E_SUBSCR) /* subscripted variable? */ { /* e.g. pvPut(xyz[i+2]); => seq_pvPut(ssId, 3 + (i+2)); */ printf(" + ("); /* evalute the subscript expression */ eval_expr(stmt_type, ep3, sp, 0); printf(")"); } /* Add any additional parameter(s) */ ep1 = ep1->next; while (ep1 != 0) { printf(", "); eval_expr(stmt_type, ep1, sp, 0); ep1 = ep1->next; } /* Close the parameter list */ printf(") \n"); return; } /* Generate exit handler code */ gen_exit_handler() { extern Expr *exit_code_list; Expr *ep; printf("/* Exit handler */\n"); printf("static void exit_handler(ssId, pVar)\n"); printf("int\tssId;\n"); printf("struct UserVar\t*pVar;\n{\n"); for (ep = exit_code_list; ep != 0; ep = ep->next) { eval_expr(EXIT_STMT, ep, 0, 1); } printf("}\n\n"); }