Skip to content
Snippets Groups Projects
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");
}