/*
* aegis - project change supervisor
* Copyright (C) 1991-1994, 1997-1999, 2001-2008 Peter Miller
* Copyright (C) 2007 Walter Franzini
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* .
*
*/
%{
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef DEBUG
#define YYDEBUG 1
extern int yydebug;
#define printf lex_debug_printf
#define fprintf lex_debug_fprintf
#endif
%}
%token BOOLEAN
%token BOOLEAN_CONSTANT
%token HIDE_IF_DEFAULT
%token INCLUDE
%token INTEGER
%token INTEGER_CONSTANT
%token NAME
%token REAL
%token REDEFINITION_OK
%token SHOW_IF_DEFAULT
%token STRING
%token STRING_CONSTANT
%token TIME
%token TYPE
%union
{
string_ty *lv_string;
long lv_integer;
type *lv_type;
}
%type NAME STRING_CONSTANT
%type INTEGER_CONSTANT attributes
%type type structure list enumeration enum_list_begin
%{
struct name_ty
{
name_ty *parent;
string_ty *name_short;
string_ty *name_long;
type *name_type;
};
static name_ty *current;
static size_t emit_length;
static size_t emit_length_max;
static type **emit_list;
static int time_used;
static symtab_ty *typedef_symtab;
static string_list_ty initialize;
static void
push_name(string_ty *s)
{
name_ty *np;
trace(("push_name(s = \"%s\")\n{\n", s->str_text));
np = (name_ty *)mem_alloc(sizeof(name_ty));
np->name_short = str_copy(s);
np->name_long =
str_format("%s_%s", current->name_long->str_text, s->str_text);
np->parent = current;
np->name_type = 0;
current = np;
trace(("}\n"));
}
static void
push_name_abs(string_ty *s)
{
name_ty *np;
trace(("push_name_abs(s = \"%s\")\n{\n", s->str_text));
np = (name_ty *)mem_alloc(sizeof(name_ty));
np->name_short = str_copy(s);
np->name_long = str_copy(s);
np->parent = current;
np->name_type = 0;
current = np;
trace(("}\n"));
}
static void
pop_name(void)
{
name_ty *np;
trace(("pop_name()\n{\n"));
np = current;
current = np->parent;
str_free(np->name_short);
str_free(np->name_long);
mem_free(np);
trace(("}\n"));
}
static void
define_type(type *defined_type)
{
trace(("define_type(defined_type = %08lX)\n{\n", (long)defined_type));
if (emit_length >= emit_length_max)
{
size_t new_emit_length_max = emit_length_max * 2 + 8;
type **new_emit_list = new type * [new_emit_length_max];
for (size_t j = 0; j < emit_length; ++j)
new_emit_list[j] = emit_list[j];
delete [] emit_list;
emit_list = new_emit_list;
emit_length_max = new_emit_length_max;
}
emit_list[emit_length++] = defined_type;
trace(("}\n"));
}
static const char *
base_name(const char *s)
{
static char buffer[256];
const char *cp;
char *bp;
cp = strrchr(s, '/');
if (cp)
++cp;
else
cp = s;
strendcpy(buffer, cp, buffer + sizeof(buffer));
bp = strrchr(buffer, '.');
if (bp)
*bp = 0;
for (bp = buffer; *bp; ++bp)
{
if (!isalnum((unsigned char)*bp))
*bp = '_';
}
return buffer;
}
static const char *
calculate_include_define_name(const char *s)
{
char *bp;
static char buffer[256];
bp = buffer;
while (*s && bp < ENDOF(buffer) - 1)
{
unsigned char c;
c = *s++;
if (!c)
break;
if (islower(c))
c = toupper(c);
else if (!isalnum(c))
c = '_';
*bp++ = c;
}
*bp = 0;
return buffer;
}
static void
this_file_is_generated(const char *definition_file)
{
/*
* DO NOT insert a timestamp in the generated files, it needlessly
* changes the fingerprint. This, in turn, causes cook to cook
* too much, compiling several hundred files which do not need to
* be compiled.
*/
indent_printf
(
"//\n"
"// This file is generated by %s from \"%s\".\n"
"// If you want to change the contents of this file\n"
"// you need to edit %s\n"
"// or you need to enhance %s.\n"
"//\n",
progname_get(),
definition_file,
definition_file,
progname_get()
);
}
static void
generate_include_file(const char *include_file, const char *definition_file)
{
const char *cp1;
size_t j;
string_ty *s;
trace(("generate_include_file(h = \"%s\")\n{\n", include_file));
s = current->name_long;
indent_open(include_file);
this_file_is_generated(definition_file);
cp1 = calculate_include_define_name(include_file);
indent_putchar('\n');
indent_printf("#ifndef %s\n", cp1);
indent_printf("#define %s\n", cp1);
indent_putchar('\n');
if (time_used)
{
indent_printf("#include \n");
indent_putchar('\n');
}
indent_printf("#include \n");
indent_printf("#include \n");
indent_printf("#include \n");
indent_printf("#include \n");
indent_printf("#include \n");
indent_putchar('\n');
indent_printf("class nstring; // forward\n");
for (j = 0; j < emit_length; ++j)
emit_list[j]->gen_include();
indent_putchar('\n');
indent_printf("/**\n");
indent_printf(" * The %s_write_file function is used to\n", s->str_text);
indent_printf(" * write %s meta data to the named file.\n", s->str_text);
indent_printf(" *\n");
indent_printf(" * @param filename\n");
indent_printf(" * The name of the file to be written.\n");
indent_printf(" * @param value\n");
indent_printf(" * The value of the meta-data to be written.\n");
indent_printf(" * @param comp\n");
indent_printf(" * true (non-zero) if data should be compressed.\n");
indent_printf(" * @note\n");
indent_printf(" * If any errors are encountered, this\n");
indent_printf(" * function will not return. All errors\n");
indent_printf(" * will print a fatal error message, and\n");
indent_printf(" * exit with an exit status of 1.\n");
indent_printf(" */\n");
indent_printf
(
"void %s_write_file(string_ty *filename, %s_ty *value, int comp);\n",
s->str_text,
s->str_text
);
indent_putchar('\n');
indent_printf("/**\n");
indent_printf(" * The %s_write_file function is used to\n", s->str_text);
indent_printf(" * write %s meta data to the named file.\n", s->str_text);
indent_printf(" *\n");
indent_printf(" * @param filnam\n");
indent_printf(" * The name of the file to be written.\n");
indent_printf(" * @param value\n");
indent_printf(" * The value of the meta-data to be written.\n");
indent_printf(" * @param comp\n");
indent_printf(" * true if data should be compressed.\n");
indent_printf(" * @note\n");
indent_printf(" * If any errors are encountered, this\n");
indent_printf(" * function will not return. All errors\n");
indent_printf(" * will print a fatal error message, and\n");
indent_printf(" * exit with an exit status of 1.\n");
indent_printf(" */\n");
indent_printf
(
"void %s_write_file(const nstring &filnam, %s_ty *value, bool comp);\n",
s->str_text,
s->str_text
);
indent_putchar('\n');
indent_printf("/**\n");
indent_printf(" * The %s_read_file function is used to\n", s->str_text);
indent_printf(" * read %s meta data from the named file.\n", s->str_text);
indent_printf(" *\n");
indent_printf(" * @param filename\n");
indent_printf(" * The name of the file to be read.\n");
indent_printf(" * @returns\n");
indent_printf(" * a pointer to a dynamically allocated\n");
indent_printf(" * value read from the file.\n");
indent_printf(" * @note\n");
indent_printf(" * If any errors are encountered, this\n");
indent_printf(" * function will not return. All errors\n");
indent_printf(" * (including syntax errors) will print a\n");
indent_printf(" * fatal error message, and exit with an\n");
indent_printf(" * exit status of 1.\n");
indent_printf(" */\n");
indent_printf
(
"%s_ty *%s_read_file(string_ty *filename);\n",
s->str_text,
s->str_text
);
indent_putchar('\n');
indent_printf("/**\n");
indent_printf(" * The %s_read_file function is used to\n", s->str_text);
indent_printf(" * read %s meta data from the named file.\n", s->str_text);
indent_printf(" *\n");
indent_printf(" * @param filename\n");
indent_printf(" * The name of the file to be read.\n");
indent_printf(" * @returns\n");
indent_printf(" * a pointer to a dynamically allocated\n");
indent_printf(" * value read from the file.\n");
indent_printf(" * @note\n");
indent_printf(" * If any errors are encountered, this\n");
indent_printf(" * function will not return. All errors\n");
indent_printf(" * (including syntax errors) will print a\n");
indent_printf(" * fatal error message, and exit with an\n");
indent_printf(" * exit status of 1.\n");
indent_printf(" */\n");
indent_printf
(
"%s_ty *%s_read_file(const nstring &filename);\n",
s->str_text,
s->str_text
);
indent_printf("void %s__rpt_init(void);\n", s->str_text);
indent_putchar('\n');
indent_printf("#endif // %s\n", cp1);
indent_close();
trace(("}\n"));
}
static void
generate_code_file(const char *code_file, const char *include_file,
const char *definition_file)
{
size_t j;
string_ty *s;
trace(("generate_code_file(c = \"%s\", h = \"%s\")\n{\n", code_file,
include_file));
s = current->name_short;
indent_open(code_file);
this_file_is_generated(definition_file);
indent_putchar('\n');
indent_printf("#include \n");
indent_printf("#include \n");
indent_putchar('\n');
indent_printf("#include \n");
indent_printf("#include \n");
indent_printf("#include \n");
indent_printf("#include \n");
indent_printf("#include \n");
indent_printf("#include \n");
indent_printf("#include \n");
indent_printf("#include \n");
indent_printf("#include \n");
indent_printf("#include <%s>\n", include_file);
const char *cp1 = base_name(code_file);
for (j = 0; j < emit_length; ++j)
{
type *tp = emit_list[j];
if (!tp->is_in_include_file())
tp->gen_code();
}
indent_putchar('\n');
indent_printf("%s_ty *\n", cp1);
indent_printf("%s_read_file(const nstring &filename)\n", s->str_text);
indent_printf("{\n");
indent_printf("return %s_read_file(filename.get_ref());\n", s->str_text);
indent_printf("}\n");
indent_putchar('\n');
indent_printf("%s_ty *\n", cp1);
indent_printf("%s_read_file(string_ty *filename)\n", s->str_text);
indent_printf("{\n");
indent_printf("%s_ty\1*result;\n\n", cp1);
indent_printf
(
"trace((\"%s_read_file(filename = \\\"%%s\\\")\\n{\\n\", "
"(filename ? filename->str_text : \"\")));\n",
cp1
);
indent_printf("os_become_must_be_active();\n");
indent_printf
(
"result = (%s_ty *)parse(filename, &%s_type);\n",
cp1,
s->str_text
);
indent_printf("trace((\"return %%08lX;\\n\", (long)result));\n");
indent_printf("trace((\"}\\n\"));\n");
indent_printf("return result;\n");
indent_printf("}\n");
indent_putchar('\n');
indent_printf("void\n");
indent_printf
(
"%s_write_file(const nstring &filename, %s_ty *value, "
"bool comp)\n",
s->str_text,
s->str_text
);
indent_printf("{\n");
indent_printf
(
"%s_write_file(filename.get_ref(), value, comp);\n",
s->str_text
);
indent_printf("}\n");
indent_putchar('\n');
indent_printf("void\n");
indent_printf
(
"%s_write_file(string_ty *filename, %s_ty *value, "
"int needs_compression)\n",
s->str_text,
s->str_text
);
indent_printf("{\n");
indent_printf
(
"trace((\"%s_write_file(filename = \\\"%%s\\\", value = %%08lX)\\n"
"{\\n\", (filename ? filename->str_text : \"\"), (long)value));\n",
cp1
);
indent_printf("if (filename)\n");
indent_more();
indent_printf("os_become_must_be_active();\n");
indent_less();
indent_printf("output::pointer fp;\n");
indent_printf("if (needs_compression)\n{\n");
indent_printf("fp = output_file::binary_open(filename);\n");
indent_printf("fp = output_gzip::create(fp);\n");
indent_printf("}\nelse\n{\n");
indent_printf("fp = output_file::text_open(filename);\n");
indent_printf("}\n");
indent_printf("fp = output_indent::create(fp);\n");
indent_printf("io_comment_emit(fp);\n");
indent_printf("%s_write(fp, value);\n", s->str_text);
indent_printf("type_enum_option_clear();\n");
indent_printf("trace((\"}\\n\"));\n");
indent_printf("}\n");
indent_putchar('\n');
indent_printf("void\n");
indent_printf("%s__rpt_init(void)\n", s->str_text);
indent_printf("{\n");
indent_printf("trace((\"%s__rpt_init()\\n{\\n\"));\n", cp1);
for (j = 0; j < initialize.nstrings; ++j)
indent_printf("%s\n", initialize.string[j]->str_text);
indent_printf("trace((\"}\\n\"));\n");
indent_printf("}\n");
indent_close();
trace(("}\n"));
}
void
generate_code__init(const nstring &s)
{
initialize.push_back(s.get_ref());
}
void
parse(const char *definition_file, const char *code_file,
const char *include_file)
{
string_ty *s;
extern int yyparse(void);
/*
* initial name is the basename of the definition file
*/
trace(("parse(def = \"%s\", c = \"%s\", h = \"%s\")\n{\n",
definition_file, code_file, include_file));
#ifdef DEBUG
yydebug = trace_pretest_;
#endif
s = str_from_c(base_name(definition_file));
push_name_abs(s);
str_free(s);
typedef_symtab = symtab_alloc(10);
/*
* parse the definition file
*/
lex_open(definition_file);
trace(("yyparse()\n{\n"));
yyparse();
trace(("}\n"));
lex_close();
/*
* remember to emit a structure containing its fields
*/
current->name_type->toplevel();
define_type(current->name_type);
/*
* generate the files
*/
generate_include_file(include_file, definition_file);
generate_code_file(code_file, include_file, definition_file);
pop_name();
trace(("}\n"));
}
%}
%%
description
: typedef_list field_list
;
typedef_list
: /* empty */
| typedef_list typedef
;
typedef
: TYPE type_name '=' type ';'
{
$4->typedef_set();
symtab_assign(typedef_symtab, current->name_long, $4);
pop_name();
if (lex_in_include_file())
$4->in_include_file();
}
| '#' INCLUDE STRING_CONSTANT
{
lex_open($3->str_text);
str_free($3);
}
| error
;
type_name
: NAME
{
push_name_abs($1);
}
;
field
: field_name '=' type attributes ';'
{
current->parent->name_type->member_add
(
nstring(current->name_short),
$3,
$4
);
pop_name();
}
| field_name error
{
pop_name();
}
;
field_name
: NAME
{
push_name($1);
str_free($1);
}
;
type
: STRING
{
$$ = new type_string();
}
| BOOLEAN
{
$$ = new type_boolean();
}
| INTEGER
{
$$ = new type_integer();
}
| REAL
{
$$ = new type_real();
}
| TIME
{
time_used = 1;
$$ = new type_time();
}
| NAME
{
type *data = (type *)symtab_query(typedef_symtab, $1);
if (data)
$$ = data;
else
{
yyerror("type \"%s\" undefined", $1->str_text);
$$ = new type_integer();
}
str_free($1);
}
| structure
{
$$ = $1;
define_type($$);
}
| list
{
$$ = $1;
define_type($$);
}
| enumeration
{
$$ = $1;
define_type($$);
}
;
structure
: '{' field_list '}'
{
$$ = current->name_type;
}
;
field_list
: /* empty */
{
current->name_type =
new type_structure(nstring(current->name_long));
}
| field_list field
;
list
: '[' type ']'
{
static string_ty *list;
if (!list)
list = str_from_c("list");
push_name(list);
$$ = new type_list(nstring(current->name_long), $2);
pop_name();
}
;
enumeration
: '(' enum_list_begin enum_list optional_comma ')'
{
$$ = $2;
}
;
enum_list_begin
: /* empty */
{
$$ = new type_enumeration(nstring(current->name_long));
current->name_type = $$;
}
;
enum_list
: enum_name
| enum_list ',' enum_name
;
enum_name
: NAME
{
push_name($1);
str_free($1);
current->parent->name_type->member_add
(
nstring(current->name_short),
(type *)0,
1
);
pop_name();
}
;
optional_comma
: /* empty */
| ','
;
attributes
: /* empty */
{ $$ = 0; }
| attributes REDEFINITION_OK
{ $$ = $1 | ATTRIBUTE_REDEFINITION_OK; }
| attributes SHOW_IF_DEFAULT
{ $$ = $1 | ATTRIBUTE_SHOW_IF_DEFAULT; }
| attributes HIDE_IF_DEFAULT
{ $$ = $1 | ATTRIBUTE_HIDE_IF_DEFAULT; }
;