%{
#include <vector>
#include <set>
#include <string>
#include <cstring>

using namespace std;

#include <stdio.h>
#include <stdlib.h>

#include "module_util.h"

using namespace std;

extern int line_number;
extern char* input_filename;
extern char* plugin;

#define print_line_directive(fp) fprintf(fp, "\n#line %d \"%s\"\n", line_number, input_filename)

extern FILE* fp_bro_init;
extern FILE* fp_func_def;
extern FILE* fp_func_h;
extern FILE* fp_func_init;
extern FILE* fp_netvar_h;
extern FILE* fp_netvar_def;
extern FILE* fp_netvar_init;

int in_c_code = 0;
string current_module = GLOBAL_MODULE_NAME;
int definition_type;
string type_name;

// Alternate event prototypes are only written to the .zeek file, but
// don't need any further changes to C++ source/header files, so this
// set keeps track of whether the first event prototype information has
// already been defined/written to the C++ files.
static std::set<std::string> events;

enum {
	C_SEGMENT_DEF,
	FUNC_DEF,
	EVENT_DEF,
	TYPE_DEF,
	CONST_DEF,
};

// Holds the name of a declared object (function, enum, record type, event,
// etc. and information about namespaces, etc.
struct decl_struct {
	string module_name;
	string bare_name; // name without module or namespace
	string c_namespace_start; // "opening" namespace for use in netvar_*
	string c_namespace_end;   // closing "}" for all the above namespaces
	string c_fullname; // fully qualified name (namespace::....) for use in netvar_init
	string bro_fullname; // fully qualified bro name, for netvar (and lookup_ID())
	string bro_name;  // the name as we read it from input. What we write into the .zeek file

	// special cases for events. Events have an EventHandlerPtr
	// and a generate_* function. This name is for the generate_* function
	string generate_bare_name;
	string generate_c_fullname;
	string generate_c_namespace_start;
	string generate_c_namespace_end;
	string enqueue_c_barename;
	string enqueue_c_fullname;
} decl;

void set_definition_type(int type, const char *arg_type_name)
	{
	definition_type = type;
	if ( type == TYPE_DEF && arg_type_name )
		type_name = string(arg_type_name);
	else
		type_name = "";
	}

void set_decl_name(const char *name)
	{
	decl.bare_name = extract_var_name(name);

	// make_full_var_name prepends the correct module, if any
	// then we can extract the module name again.
	string varname = make_full_var_name(current_module.c_str(), name);
	decl.module_name = extract_module_name(varname.c_str());

	decl.c_namespace_start = "";
	decl.c_namespace_end = "";
	decl.c_fullname = "";
	decl.bro_fullname = "";
	decl.bro_name = "";

	decl.generate_c_fullname = "";
	decl.enqueue_c_fullname = "";
	decl.generate_bare_name = string("generate_") + decl.bare_name;
	decl.enqueue_c_barename = string("enqueue_") + decl.bare_name;
	decl.generate_c_namespace_start = "";
	decl.generate_c_namespace_end = "";

	switch ( definition_type ) {
	case TYPE_DEF:
		decl.c_namespace_start = "namespace BifType { namespace " + type_name + "{ ";
		decl.c_namespace_end = " } }";
		decl.c_fullname = "BifType::" + type_name + "::";
		break;

	case CONST_DEF:
		decl.c_namespace_start = "namespace BifConst { ";
		decl.c_namespace_end = " } ";
		decl.c_fullname = "BifConst::";
		break;

	case FUNC_DEF:
		decl.c_namespace_start = "namespace BifFunc { ";
		decl.c_namespace_end = " } ";
		decl.c_fullname = "BifFunc::";
		break;

	case EVENT_DEF:
		decl.c_namespace_start = "";
		decl.c_namespace_end = "";
		decl.c_fullname = "::";  // need this for namespace qualified events due do event_c_body
		decl.generate_c_namespace_start = "namespace BifEvent { ";
		decl.generate_c_namespace_end = " } ";
		decl.generate_c_fullname = "BifEvent::";
		decl.enqueue_c_fullname = "zeek::BifEvent::";
		break;

	default:
		break;
	}

	if ( decl.module_name != GLOBAL_MODULE_NAME )
		{
		decl.c_namespace_start += "namespace " + decl.module_name + " { ";
		decl.c_namespace_end += string(" }");
		decl.c_fullname += decl.module_name + "::";
		decl.bro_fullname += decl.module_name + "::";

		decl.generate_c_namespace_start  += "namespace " + decl.module_name + " { ";
		decl.generate_c_namespace_end += " } ";
		decl.generate_c_fullname += decl.module_name + "::";
		decl.enqueue_c_fullname += decl.module_name + "::";
		}

	decl.bro_fullname += decl.bare_name;
	decl.c_fullname += decl.bare_name;
	decl.bro_name += name;
	decl.generate_c_fullname += decl.generate_bare_name;
	decl.enqueue_c_fullname += decl.enqueue_c_barename;
	}

const char* arg_list_name = "BiF_ARGS";

#include "bif_arg.h"

/* Map bif/bro type names to C types for use in const declaration */
static struct {
	const char* bif_type;
	const char* bro_type;
	const char* c_type;
	const char* c_type_smart;
	const char* accessor;
	const char* accessor_smart;
	const char* cast_smart;
	const char* constructor;
	const char* ctor_smatr;
} builtin_types[] = {
#define DEFINE_BIF_TYPE(id, bif_type, bro_type, c_type, c_type_smart, accessor, accessor_smart, cast_smart, constructor, ctor_smart) \
	{bif_type, bro_type, c_type, c_type_smart, accessor, accessor_smart, cast_smart, constructor, ctor_smart},
#include "bif_type.def"
#undef DEFINE_BIF_TYPE
};

int get_type_index(const char *type_name)
	{
	for ( int i = 0; builtin_types[i].bif_type[0] != '\0'; ++i )
		{
		if ( strcmp(builtin_types[i].bif_type, type_name) == 0 )
			return i;
		}
		return TYPE_OTHER;
	}


int var_arg; // whether the number of arguments is variable
std::vector<BuiltinFuncArg*> args;

extern int yyerror(const char[]);
extern int yywarn(const char msg[]);
extern int yylex();

char* concat(const char* str1, const char* str2)
	{
	int len1 = strlen(str1);
	int len2 = strlen(str2);

	char* s = new char[len1 + len2 +1];

	memcpy(s, str1, len1);
	memcpy(s + len1, str2, len2);

	s[len1+len2] = '\0';

	return s;
	}

static void print_event_c_prototype_args(FILE* fp, bool smart)
	{
	for ( auto i = 0u; i < args.size(); ++i )
		{
		if ( i > 0 )
			fprintf(fp, ", ");

		args[i]->PrintCArg(fp, i, smart);
		}
	}

static void print_event_c_prototype_header(FILE* fp, bool smart)
	{
	if ( smart )
		fprintf(fp, "namespace zeek { %s void %s(analyzer::Analyzer* analyzer%s",
		        decl.generate_c_namespace_start.c_str(),
		        decl.enqueue_c_barename.c_str(),
		        args.size() ? ", " : "" );
	else
		fprintf(fp, "%s [[deprecated(\"Remove in 4.1. Use %s.\")]] void %s(analyzer::Analyzer* analyzer%s",
		        decl.generate_c_namespace_start.c_str(),
		        decl.enqueue_c_fullname.c_str(),
		        decl.generate_bare_name.c_str(),
		        args.size() ? ", " : "" );


	print_event_c_prototype_args(fp, smart);
	fprintf(fp, ")");
	fprintf(fp, "; %s%s\n", decl.generate_c_namespace_end.c_str(), smart ? " }" : "");
	}

static void print_event_c_prototype_impl(FILE* fp, bool smart)
	{
	if ( smart )
		fprintf(fp, "void %s(analyzer::Analyzer* analyzer%s",
			decl.enqueue_c_fullname.c_str(),
			args.size() ? ", " : "" );
	else
		fprintf(fp, "void %s(analyzer::Analyzer* analyzer%s",
			decl.generate_c_fullname.c_str(),
			args.size() ? ", " : "" );

	print_event_c_prototype_args(fp, smart);
	fprintf(fp, ")");
	fprintf(fp, "\n");
	}

static void print_event_c_body(FILE* fp, bool smart)
	{
	fprintf(fp, "\t{\n");
	fprintf(fp, "\t// Note that it is intentional that here we do not\n");
	fprintf(fp, "\t// check if %s is NULL, which should happen *before*\n",
		decl.c_fullname.c_str());
	fprintf(fp, "\t// %s is called to avoid unnecessary Val\n",
		decl.generate_c_fullname.c_str());
	fprintf(fp, "\t// allocation.\n");
	fprintf(fp, "\n");

	BuiltinFuncArg* connection_arg = 0;

	fprintf(fp, "\tmgr.Enqueue(%s, zeek::Args{\n", decl.c_fullname.c_str());

	for ( int i = 0; i < (int) args.size(); ++i )
		{
		fprintf(fp, "\t        ");
		args[i]->PrintBroValConstructor(fp, smart);
		fprintf(fp, ",\n");

		if ( args[i]->Type() == TYPE_CONNECTION )
			{
			if ( connection_arg == 0 )
				connection_arg = args[i];
			else
				{
				// We are seeing two connection type arguments.
				yywarn("Warning: with more than connection-type "
				       "event arguments, bifcl only passes "
				       "the first one to EventMgr as cookie.");
				}
			}
		}

	fprintf(fp, "\t        },\n\t    SOURCE_LOCAL, analyzer->GetID()");

	if ( connection_arg )
		// Pass the connection to the EventMgr as the "cookie"
		fprintf(fp, ", %s", connection_arg->Name());

	fprintf(fp, ");\n");
	fprintf(fp, "\t}\n\n");
	//fprintf(fp, "%s // end namespace\n", decl.generate_c_namespace_end.c_str());
	}

void record_bif_item(const char* id, const char* type)
	{
	if ( ! plugin )
		return;

	fprintf(fp_func_init, "\tplugin->AddBifItem(\"%s\", zeek::plugin::BifItem::%s);\n", id, type);
	}

%}

%token TOK_LPP TOK_RPP TOK_LPB TOK_RPB TOK_LPPB TOK_RPPB TOK_VAR_ARG
%token TOK_BOOL
%token TOK_FUNCTION TOK_EVENT TOK_CONST TOK_ENUM TOK_OF
%token TOK_TYPE TOK_RECORD TOK_SET TOK_VECTOR TOK_OPAQUE TOK_TABLE TOK_MODULE
%token TOK_ARGS TOK_ARG TOK_ARGC
%token TOK_ID TOK_ATTR TOK_CSTR TOK_LF TOK_WS TOK_COMMENT
%token TOK_ATOM TOK_INT TOK_C_TOKEN

%left ',' ':'

%type <str> TOK_C_TOKEN TOK_ID TOK_CSTR TOK_WS TOK_COMMENT TOK_ATTR TOK_INT opt_ws type attr_list opt_attr_list opt_func_attrs
%type <val> TOK_ATOM TOK_BOOL

%union	{
	const char* str;
	int val;
}

%%

builtin_lang:	definitions
			{
			fprintf(fp_bro_init, "} # end of export section\n");
			fprintf(fp_bro_init, "module %s;\n", GLOBAL_MODULE_NAME);
			}



definitions:	definitions definition opt_ws
			{
			if ( in_c_code )
				fprintf(fp_func_def, "%s", $3);
			else
				fprintf(fp_bro_init, "%s", $3);
			}
	|	opt_ws
			{
			fprintf(fp_bro_init, "%s", $1);
			fprintf(fp_bro_init, "export {\n");
			}
	;

definition:	event_def
	|	func_def
	|	c_code_segment
	|	enum_def
	|	const_def
	|	type_def
	|   module_def
	;


module_def:	TOK_MODULE opt_ws TOK_ID opt_ws ';'
			{
			current_module = string($3);
			fprintf(fp_bro_init, "module %s;\n", $3);
			}

	 // XXX: Add the netvar glue so that the event engine knows about
	 // the type. One still has to define the type in bro.init.
	 // Would be nice, if we could just define the record type here
	 // and then copy to the .bif.zeek file, but type declarations in
	 // Zeek can be quite powerful. Don't know whether it's worth it
	 // extend the bif-language to be able to handle that all....
	 // Or we just support a simple form of record type definitions
	 // TODO: add other types (tables, sets)
type_def:	TOK_TYPE opt_ws TOK_ID opt_ws ':' opt_ws type_def_types opt_ws ';'
			{
			set_decl_name($3);

			fprintf(fp_netvar_h, "namespace zeek { %s extern zeek::IntrusivePtr<zeek::%sType> %s; %s}\n",
				decl.c_namespace_start.c_str(), type_name.c_str(),
				decl.bare_name.c_str(), decl.c_namespace_end.c_str());
			fprintf(fp_netvar_h, "%s [[deprecated(\"Remove in v4.1.  Use zeek::%s.\")]] extern zeek::%sType * %s; %s\n",
				decl.c_namespace_start.c_str(), decl.c_fullname.c_str(), type_name.c_str(),
				decl.bare_name.c_str(), decl.c_namespace_end.c_str());


			fprintf(fp_netvar_def, "namespace zeek { %s zeek::IntrusivePtr<zeek::%sType> %s; %s}\n",
				decl.c_namespace_start.c_str(), type_name.c_str(),
				decl.bare_name.c_str(), decl.c_namespace_end.c_str());
			fprintf(fp_netvar_def, "%s zeek::%sType * %s; %s\n",
				decl.c_namespace_start.c_str(), type_name.c_str(),
				decl.bare_name.c_str(), decl.c_namespace_end.c_str());

			fprintf(fp_netvar_init,
				"\tzeek::%s = zeek::id::find_type<zeek::%sType>(\"%s\");\n",
				decl.c_fullname.c_str(), type_name.c_str(),
				decl.bro_fullname.c_str());
			fprintf(fp_netvar_init,
				"\t%s = zeek::%s.get();\n",
				decl.c_fullname.c_str(), decl.c_fullname.c_str());

			record_bif_item(decl.bro_fullname.c_str(), "TYPE");
			}
	;

type_def_types: TOK_RECORD
			{ set_definition_type(TYPE_DEF, "Record"); }
	| TOK_SET
			{ set_definition_type(TYPE_DEF, "Set"); }
	| TOK_VECTOR
			{ set_definition_type(TYPE_DEF, "Vector"); }
	| TOK_TABLE
			{ set_definition_type(TYPE_DEF, "Table"); }
	;

opt_func_attrs:	attr_list opt_ws
		{ $$ = $1; }
	| /* nothing */
		{ $$ = ""; }
	;

event_def:	event_prefix opt_ws plain_head opt_func_attrs
			{ fprintf(fp_bro_init, "%s", $4); } end_of_head ';'
			{
			if ( events.find(decl.bro_fullname) == events.end() )
				{
				print_event_c_prototype_header(fp_func_h, false);
				print_event_c_prototype_impl(fp_func_def, false);
				print_event_c_body(fp_func_def, false);
				print_event_c_prototype_header(fp_func_h, true);
				print_event_c_prototype_impl(fp_func_def, true);
				print_event_c_body(fp_func_def, true);
				events.insert(decl.bro_fullname);
				}
			}

func_def:	func_prefix opt_ws typed_head opt_func_attrs
			{ fprintf(fp_bro_init, "%s", $4); } end_of_head body
	;

enum_def:	enum_def_1 enum_list TOK_RPB opt_attr_list
			{
			// First, put an end to the enum type decl.
			fprintf(fp_bro_init, "} ");
			fprintf(fp_bro_init, "%s", $4);
			fprintf(fp_bro_init, ";\n");
			if ( decl.module_name != GLOBAL_MODULE_NAME )
				fprintf(fp_netvar_h, "}; } }\n");
			else
				fprintf(fp_netvar_h, "}; }\n");

			// Now generate the netvar's.
			fprintf(fp_netvar_h, "namespace zeek { %s extern zeek::IntrusivePtr<zeek::EnumType> %s; %s}\n",
				decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str());
			fprintf(fp_netvar_h, "%s [[deprecated(\"Remove in v4.1.  Use zeek::%s.\")]] extern zeek::EnumType * %s; %s\n",
				decl.c_namespace_start.c_str(), decl.c_fullname.c_str(),
				decl.bare_name.c_str(), decl.c_namespace_end.c_str());

			fprintf(fp_netvar_def, "namespace zeek { %s zeek::IntrusivePtr<zeek::EnumType> %s; %s}\n",
				decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str());
			fprintf(fp_netvar_def, "%s zeek::EnumType * %s; %s\n",
				decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str());

			fprintf(fp_netvar_init,
				"\tzeek::%s = zeek::id::find_type<zeek::EnumType>(\"%s\");\n",
				decl.c_fullname.c_str(), decl.bro_fullname.c_str());
			fprintf(fp_netvar_init,
				"\t%s = zeek::%s.get();\n",
				decl.c_fullname.c_str(), decl.c_fullname.c_str());

			record_bif_item(decl.bro_fullname.c_str(), "TYPE");
			}
	;

enum_def_1:	TOK_ENUM opt_ws TOK_ID opt_ws TOK_LPB opt_ws
			{
			set_definition_type(TYPE_DEF, "Enum");
			set_decl_name($3);
			fprintf(fp_bro_init, "type %s: enum %s{%s", decl.bro_name.c_str(), $4, $6);

			// this is the namespace were the enumerators are defined, not where
			// the type is defined.
			// We don't support fully qualified names as enumerators. Use a module name
			fprintf(fp_netvar_h, "namespace BifEnum { ");
			if ( decl.module_name != GLOBAL_MODULE_NAME )
				fprintf(fp_netvar_h, "namespace %s { ", decl.module_name.c_str());
			fprintf(fp_netvar_h, "enum %s {\n", $3);
			}
	;

enum_list:	enum_list TOK_ID opt_ws ',' opt_ws
			{
			fprintf(fp_bro_init, "%s%s,%s", $2, $3, $5);
			fprintf(fp_netvar_h, "\t%s,\n", $2);
			}
	| 		enum_list TOK_ID opt_ws '=' opt_ws TOK_INT opt_ws ',' opt_ws
			{
			fprintf(fp_bro_init, "%s = %s%s,%s", $2, $6, $7, $9);
			fprintf(fp_netvar_h, "\t%s = %s,\n", $2, $6);
			}
	|	/* nothing */
	;


const_def:	TOK_CONST opt_ws TOK_ID opt_ws ':' opt_ws TOK_ID opt_ws ';'
			{
			set_definition_type(CONST_DEF, 0);
			set_decl_name($3);
			int typeidx = get_type_index($7);
			char accessor[1024];
			char accessor_smart[1024];

			snprintf(accessor, sizeof(accessor), builtin_types[typeidx].accessor, "");
			snprintf(accessor_smart, sizeof(accessor_smart), builtin_types[typeidx].accessor_smart, "");


			fprintf(fp_netvar_h, "namespace zeek { %s extern %s %s; %s }\n",
					decl.c_namespace_start.c_str(),
					builtin_types[typeidx].c_type_smart, decl.bare_name.c_str(),
					decl.c_namespace_end.c_str());
			fprintf(fp_netvar_h, "%s [[deprecated(\"Remove in v4.1.  Use zeek::%s.\")]] extern %s %s; %s\n",
					decl.c_namespace_start.c_str(), decl.c_fullname.c_str(),
					builtin_types[typeidx].c_type, decl.bare_name.c_str(),
					decl.c_namespace_end.c_str());

			fprintf(fp_netvar_def, "namespace zeek { %s %s %s; %s }\n",
					decl.c_namespace_start.c_str(),
					builtin_types[typeidx].c_type_smart, decl.bare_name.c_str(),
					decl.c_namespace_end.c_str());
			fprintf(fp_netvar_def, "%s %s %s; %s\n",
					decl.c_namespace_start.c_str(),
					builtin_types[typeidx].c_type, decl.bare_name.c_str(),
					decl.c_namespace_end.c_str());

			fprintf(fp_netvar_init, "\t{\n");
			fprintf(fp_netvar_init, "\tconst auto& v = zeek::id::find_const%s(\"%s\");\n",
					builtin_types[typeidx].cast_smart, decl.bro_fullname.c_str());
			fprintf(fp_netvar_init, "\tzeek::%s = v%s;\n",
					decl.c_fullname.c_str(), accessor_smart);
			fprintf(fp_netvar_init, "\t%s = v.get()%s;\n",
					decl.c_fullname.c_str(), accessor);
			fprintf(fp_netvar_init, "\t}\n");

			record_bif_item(decl.bro_fullname.c_str(), "CONSTANT");
			}

attr_list:
		attr_list TOK_ATTR
			{ $$ = concat($1, $2); }
	|
		TOK_ATTR
	;

opt_attr_list:
		attr_list
	|	/* nothing */
		{ $$ = ""; }
	;

func_prefix:	TOK_FUNCTION
			{ set_definition_type(FUNC_DEF, 0); }
	;

event_prefix:	TOK_EVENT
			{ set_definition_type(EVENT_DEF, 0); }
	;

end_of_head:	/* nothing */
			{
			fprintf(fp_bro_init, ";\n");
			}
	;

typed_head:	plain_head return_type
			{
			}
	;

plain_head:	head_1 args arg_end opt_ws
			{
			if ( var_arg )
				fprintf(fp_bro_init, "va_args: any");
			else
				{
				for ( int i = 0; i < (int) args.size(); ++i )
					{
					if ( i > 0 )
						fprintf(fp_bro_init, ", ");
					args[i]->PrintBro(fp_bro_init);
					}
				}

			fprintf(fp_bro_init, ")");

			fprintf(fp_bro_init, "%s", $4);
			fprintf(fp_func_def, "%s", $4);
			}
	;

head_1:		TOK_ID opt_ws arg_begin
			{
			const char* method_type = 0;
			set_decl_name($1);

			if ( definition_type == FUNC_DEF )
				{
				method_type = "function";
				print_line_directive(fp_func_def);
				}
			else if ( definition_type == EVENT_DEF )
				method_type = "event";

			if ( method_type )
				fprintf(fp_bro_init,
					"global %s: %s%s(",
					decl.bro_name.c_str(), method_type, $2);

			if ( definition_type == FUNC_DEF )
				{
				fprintf(fp_func_init,
					"\t(void) new zeek::detail::BuiltinFunc(zeek::%s_bif, \"%s\", 0);\n",
					decl.c_fullname.c_str(), decl.bro_fullname.c_str());

				// This is the "canonical" version, with argument type and order
				// mostly for historical reasons.  There's also no "bro_" prefix
				// in the function name itself, but does have a "_bif" suffix
				// to potentially help differentiate from other functions
				// (e.g. ones at global scope that may be used to implement
				// the BIF itself).
				fprintf(fp_func_h,
					"namespace zeek { %sextern BifReturnVal %s_bif(zeek::detail::Frame* frame, const zeek::Args*);%s }\n",
					decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str());

				// This is the deprecated, legacy, version of the BIF that
				// forwards to the "canonical" version.  It also does have
				// a "bro_" prefix in the name itself, but no "_bif" suffix.
				fprintf(fp_func_h,
					"%s [[deprecated(\"Remove in v4.1.  Use zeek::%s_bif.\")]] inline BifReturnVal bro_%s(zeek::detail::Frame* frame, const zeek::Args* args)",
					decl.c_namespace_start.c_str(), decl.c_fullname.c_str(), decl.bare_name.c_str());
				fprintf(fp_func_h, " { return zeek::%s_bif(frame, args); } %s\n",
					decl.c_fullname.c_str(), decl.c_namespace_end.c_str());

				fprintf(fp_func_def,
					"BifReturnVal zeek::%s_bif(zeek::detail::Frame* frame, const zeek::Args* %s)",
					decl.c_fullname.c_str(), arg_list_name);

				record_bif_item(decl.bro_fullname.c_str(), "FUNCTION");
				}
			else if ( definition_type == EVENT_DEF )
				{
				if ( events.find(decl.bro_fullname) == events.end() )
					{
					// TODO: add namespace for events here
					fprintf(fp_netvar_h,
						"%sextern EventHandlerPtr %s; %s\n",
						decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str());

					fprintf(fp_netvar_def,
						"%sEventHandlerPtr %s; %s\n",
						decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str());

					fprintf(fp_netvar_init,
						"\t%s = event_registry->Register(\"%s\");\n",
						decl.c_fullname.c_str(), decl.bro_fullname.c_str());

					record_bif_item(decl.bro_fullname.c_str(), "EVENT");
					// C++ prototypes of bro_event_* functions will
					// be generated later.
					}
				}
			}
	;

arg_begin:	TOK_LPP
			{ args.clear(); var_arg = 0; }
	;

arg_end:	TOK_RPP
	;

args:		args_1
	|	opt_ws
			{ /* empty, to avoid yacc complaint about type clash */ }
	;

args_1:		args_1 ',' opt_ws arg opt_ws opt_attr_list
			{ if ( ! args.empty() ) args[args.size()-1]->SetAttrStr($6); }
	|	opt_ws arg opt_ws opt_attr_list
			{ if ( ! args.empty() ) args[args.size()-1]->SetAttrStr($4); }
	;

// TODO: Migrate all other compound types to this rule. Once the BiF language
// can parse all regular Zeek types, we can throw out the unnecessary
// boilerplate typedefs for addr_set, string_set, etc.
type:
                TOK_OPAQUE opt_ws TOK_OF opt_ws TOK_ID
                        { $$ = concat("opaque of ", $5); }
        |       TOK_ID
                        { $$ = $1; }
        ;

arg:		TOK_ID opt_ws ':' opt_ws type
			{ args.push_back(new BuiltinFuncArg($1, $5)); }
	|	TOK_VAR_ARG
			{
			if ( definition_type == EVENT_DEF )
				yyerror("events cannot have variable arguments");
			var_arg = 1;
			}
	;

return_type:	':' opt_ws type opt_ws
			{
			BuiltinFuncArg* ret = new BuiltinFuncArg("", $3);
			ret->PrintBro(fp_bro_init);
			delete ret;
			fprintf(fp_func_def, "%s", $4);
			}
	;

body:		body_start c_body body_end
			{
			fprintf(fp_func_def, " // end of %s\n", decl.c_fullname.c_str());
			print_line_directive(fp_func_def);
			}
	;

c_code_begin:	/* empty */
			{
			in_c_code = 1;
			print_line_directive(fp_func_def);
			}
	;

c_code_end:	/* empty */
			{ in_c_code = 0; }
	;

body_start:	TOK_LPB c_code_begin
			{
			int implicit_arg = 0;
			int argc = args.size();

			fprintf(fp_func_def, "{");

			if ( argc > 0 || ! var_arg )
				fprintf(fp_func_def, "\n");

			if ( ! var_arg )
				{
				fprintf(fp_func_def, "\tif ( %s->size() != %d )\n", arg_list_name, argc);
				fprintf(fp_func_def, "\t\t{\n");
				fprintf(fp_func_def,
					"\t\treporter->Error(\"%s() takes exactly %d argument(s)\");\n",
					decl.bro_fullname.c_str(), argc);
				fprintf(fp_func_def, "\t\treturn nullptr;\n");
				fprintf(fp_func_def, "\t\t}\n");
				}
			else if ( argc > 0 )
				{
				fprintf(fp_func_def, "\tif ( %s->size() < %d )\n", arg_list_name, argc);
				fprintf(fp_func_def, "\t\t{\n");
				fprintf(fp_func_def,
					"\t\treporter->Error(\"%s() takes at least %d argument(s)\");\n",
					decl.bro_fullname.c_str(), argc);
				fprintf(fp_func_def, "\t\treturn nullptr;\n");
				fprintf(fp_func_def, "\t\t}\n");
				}

			for ( int i = 0; i < (int) args.size(); ++i )
				args[i]->PrintCDef(fp_func_def, i + implicit_arg);
			print_line_directive(fp_func_def);
			}
	;

body_end:	TOK_RPB c_code_end
			{
			fprintf(fp_func_def, "}");
			}
	;

c_code_segment: TOK_LPPB c_code_begin c_body c_code_end TOK_RPPB
	;

c_body:		opt_ws
			{ fprintf(fp_func_def, "%s", $1); }
	|	c_body c_atom opt_ws
			{ fprintf(fp_func_def, "%s", $3); }
	;

c_atom:		TOK_ID
			{ fprintf(fp_func_def, "%s", $1); }
	|	TOK_C_TOKEN
			{ fprintf(fp_func_def, "%s", $1); }
	|	TOK_ARG
			{ fprintf(fp_func_def, "(*%s)", arg_list_name); }
	|	TOK_ARGS
			{ fprintf(fp_func_def, "%s", arg_list_name); }
	|	TOK_ARGC
			{ fprintf(fp_func_def, "%s->size()", arg_list_name); }
	|	TOK_CSTR
			{ fprintf(fp_func_def, "%s", $1); }
	|	TOK_ATOM
			{ fprintf(fp_func_def, "%c", $1); }
	|	TOK_INT
			{ fprintf(fp_func_def, "%s", $1); }

	;

opt_ws:		opt_ws TOK_WS
			{ $$ = concat($1, $2); }
	|	opt_ws TOK_LF
			{ $$ = concat($1, "\n"); }
	|	opt_ws TOK_COMMENT
			{
			if ( in_c_code )
				$$ = concat($1, $2);
			else
				if ( $2[1] == '#' )
					// This is a special type of comment that is used to
					// generate bro script documentation, so pass it through.
					$$ = concat($1, $2);
				else
					$$ = $1;
			}
	|	/* empty */
			{ $$ = ""; }
	;

%%

extern char* yytext;
extern char* input_filename;
extern int line_number;
void err_exit(void);

void print_msg(const char msg[])
	{
	int msg_len = strlen(msg) + strlen(yytext) + 64;
	char* msgbuf = new char[msg_len];

	if ( yytext[0] == '\n' )
		snprintf(msgbuf, msg_len, "%s, on previous line", msg);

	else if ( yytext[0] == '\0' )
		snprintf(msgbuf, msg_len, "%s, at end of file", msg);

	else
		snprintf(msgbuf, msg_len, "%s, at or near \"%s\"", msg, yytext);

	/*
	extern int column;
	sprintf(msgbuf, "%*s\n%*s\n", column, "^", column, msg);
	*/

	if ( input_filename )
		fprintf(stderr, "%s:%d: ", input_filename, line_number);
	else
		fprintf(stderr, "line %d: ", line_number);
	fprintf(stderr, "%s\n", msgbuf);

	delete [] msgbuf;
	}

int yywarn(const char msg[])
	{
	print_msg(msg);
	return 0;
	}

int yyerror(const char msg[])
	{
	print_msg(msg);

	err_exit();
	return 0;
	}
