-
benjamin.franksen authoredbenjamin.franksen authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
gen_code.c 7.54 KiB
/*************************************************************************\
Copyright (c) 1990 The Regents of the University of California
and the University of Chicago.
Los Alamos National Laboratory
Copyright (c) 2010-2012 Helmholtz-Zentrum Berlin f. Materialien
und Energie GmbH, Germany (HZB)
This file is distributed subject to a Software License Agreement found
in the file LICENSE that is included with this distribution.
\*************************************************************************/
/*************************************************************************\
Code generation
\*************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "analysis.h"
#include "gen_ss_code.h"
#include "gen_tables.h"
#include "main.h"
#include "gen_code.h"
static void gen_main(char *prog_name);
static void gen_user_var(Expr *prog, uint opt_reent);
static void gen_extra_c_code(Expr *extra_defns);
static void gen_init_reg(char *prog_name);
static void gen_func_decls(Expr *prog);
static int assert_var_declared(Expr *ep, Expr *scope, void *parg)
{
#ifdef DEBUG
report("assert_var_declared: '%s' in scope (%s:%s)\n",
ep->value, expr_type_name(scope), scope->value);
#endif
assert(ep->type == E_VAR);
assert(ep->extra.e_var != 0);
assert(ep->extra.e_var->decl != 0 ||
ep->extra.e_var->type->tag == T_NONE);
return TRUE; /* there are no children anyway */
}
/* Generate C code from parse tree. */
void generate_code(Program *p, const char *header_name)
{
/* assume there have been no errors, so all vars are declared */
traverse_expr_tree(p->prog, bit(E_VAR), 0, 0, assert_var_declared, 0);
#ifdef DEBUG
report("-------------------- Code Generation --------------------\n");
#endif
set_gen_h();
/* Initial comments */
gen_code("/* Header file for program %s, ", p->name);
gen_code("generated by snc from %s */\n", p->prog->src_file);
/* The usual C header file humdrum */
gen_code("#ifndef INCL%sh\n", p->name);
gen_code("#define INCL%sh\n", p->name);
/* Includes */
gen_code("#include \"seqCom.h\"\n");
/* Generate literal C code intermixed with global definitions */
gen_defn_c_code(p->prog, 0);
if (!p->options.reent)
{
gen_code("/* Warning: Access to SNL variables from outside */\n");
gen_code("/* the program is only supported in reentrant mode */\n");
}
else
{
/* Generate global, state set, and state variable declarations */
gen_user_var(p->prog, p->options.reent);
}
/* The usual C header file humdrum */
gen_code("#endif /* INCL%sh */\n", p->name);
set_gen_c();
/* Initial comments */
gen_code("/* C code for program %s, ", p->name);
gen_code("generated by snc from %s */\n", p->prog->src_file);
/* Includes */
gen_code("#include <string.h>\n");
gen_code("#include <stddef.h>\n");
gen_code("#include <stdio.h>\n");
gen_code("#include <limits.h>\n");
gen_code("\n");
gen_code("#include \"seq_snc.h\"\n");
gen_code("#include \"%s\"\n", header_name);
if (!p->options.reent)
{
/* Generate global, state set, and state variable declarations */
gen_user_var(p->prog, p->options.reent);
}
/* Generate code for function definitions */
gen_func_decls(p->prog);
/* Output extra C code */
gen_extra_c_code(p->prog->prog_xdefns);
/* Generate code for each state set */
gen_ss_code(p->prog, p->options);
/* Generate tables */
gen_tables(p);
/* Generate main function */
if (p->options.main) gen_main(p->name);
/* Sequencer registration */
gen_init_reg(p->name);
}
/* Generate main program */
static void gen_main(char *prog_name)
{
gen_code("\n#define PROG_NAME %s\n", prog_name);
gen_code("#include \"seqMain.c\"\n");
}
void gen_var_decl(Var *vp)
{
gen_type(vp->type, "", vp->name);
}
static void gen_func_decls(Expr *prog)
{
Var *vp;
assert(prog->type == D_PROG);
gen_code("\n/* Function declarations */\n");
/* function declarations are always global and static */
foreach (vp, var_list_from_scope(prog)->first)
{
if (vp->decl && vp->type->tag == T_FUNCTION)
{
gen_line_marker(vp->decl);
gen_code("static ");
gen_var_decl(vp);
gen_code(";\n");
}
}
}
/* Generate the struct containing all program variables with
'infinite' (global) lifetime. These are: variables declared at the
top-level, inside a state set, and inside a state. Note that state
set and state local variables are _visible_ only inside the block
where they are declared, but still have global lifetime. To avoid
name collisions, generate a nested struct for each state set, and
for each state in a state set. */
static void gen_user_var(Expr *prog, uint opt_reent)
{
Var *vp;
Expr *sp, *ssp;
uint num_globals = 0;
uint num_decls = 0;
gen_code("\n/* Variable declarations */\n");
if (opt_reent)
{
gen_code("struct %s {\n", NM_VARS);
}
/* Convert internal type to `C' type */
foreach (vp, var_list_from_scope(prog)->first)
{
if (vp->decl && vp->type->tag != T_NONE && vp->type->tag != T_EVFLAG &&
vp->type->tag != T_FUNCTION)
{
gen_line_marker(vp->decl);
if (!opt_reent) gen_code("static");
indent(1);
gen_var_decl(vp);
num_decls++;
gen_code(";\n");
num_globals++;
}
}
if (opt_reent && !num_globals)
{
indent(1); gen_code("char _seq_dummy;\n");
}
foreach (ssp, prog->prog_statesets)
{
int level = opt_reent;
int ss_empty = !ssp->extra.e_ss->var_list->first;
if (ss_empty)
{
foreach (sp, ssp->ss_states)
{
if (sp->extra.e_state->var_list->first)
{
ss_empty = 0;
break;
}
}
}
if (!ss_empty)
{
indent(level); gen_code("struct %s_%s {\n", NM_VARS, ssp->value);
foreach (vp, ssp->extra.e_ss->var_list->first)
{
indent(level+1); gen_var_decl(vp); gen_code(";\n");
num_decls++;
}
foreach (sp, ssp->ss_states)
{
int s_empty = !sp->extra.e_state->var_list->first;
if (!s_empty)
{
indent(level+1);
gen_code("struct {\n");
foreach (vp, sp->extra.e_state->var_list->first)
{
indent(level+2); gen_var_decl(vp); gen_code(";\n");
num_decls++;
}
indent(level+1);
gen_code("} %s_%s;\n", NM_VARS, sp->value);
}
}
indent(level); gen_code("} %s_%s", NM_VARS, ssp->value);
gen_code(";\n");
}
}
if (opt_reent)
{
if (!num_decls)
{
indent(1); gen_code("char dummy;\n");
}
gen_code("};\n");
}
gen_code("\n");
}
/* Generate C code in definition section */
void gen_defn_c_code(Expr *scope, int level)
{
Expr *ep;
int first = TRUE;
Expr *defn_list = defn_list_from_scope(scope);
foreach (ep, defn_list)
{
if (ep->type == T_TEXT)
{
if (first)
{
first = FALSE;
indent(level);
gen_code("/* C code definitions */\n");
}
gen_line_marker(ep);
indent(level);
gen_code("%s\n", ep->value);
}
}
}
/* Generate extra C code following state sets */
static void gen_extra_c_code(Expr *extra_defns)
{
Expr *ep;
if (extra_defns != 0)
{
gen_code("\n/* Extra escaped C code */\n");
foreach (ep, extra_defns)
{
if (ep->type == T_TEXT)
{
gen_line_marker(ep);
gen_code("%s\n", ep->value);
}
}
}
}
static void gen_init_reg(char *prog_name)
{
gen_code("\n/* Register sequencer commands and program */\n");
gen_code("#include \"epicsExport.h\"\n");
gen_code("static void %sRegistrar (void) {\n", prog_name);
gen_code(" seqRegisterSequencerCommands();\n");
gen_code(" seqRegisterSequencerProgram (&%s);\n", prog_name);
gen_code("}\n");
gen_code("epicsExportRegistrar(%sRegistrar);\n", prog_name);
}
void indent(int level)
{
while (level-- > 0)
gen_code("\t");
}