/*
 * Copyright (c) 1995, 1996, 1997, 1998, 1999 The University of Utah and
 * the Computer Systems Laboratory at the University of Utah (CSL).
 *
 * This file is part of Flick, the Flexible IDL Compiler Kit.
 *
 * Flick 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 2 of the License, or
 * (at your option) any later version.
 *
 * Flick 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 Flick; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place #330, Boston, MA 02111, USA.
 */

#include <assert.h>
#include <string.h>

#include <mom/compiler.h>
#include <mom/libaoi.h>
#include <mom/c/libcast.h>
#include <mom/c/pfe.hh>

/*
 * This file contains `calc_name': the basic `pg_state' function that is used
 * to determine the names of most (someday, all) of the components that make up
 * a presentation.  These presentation components include client stubs, server
 * stubs, marshal/unmarshal stubs, data types, parameters, ... --- in short,
 * every piece of a presentation that has a name!
 *
 * `calc_name' is driven by `printf'-like format strings that define how the
 * names of various presentation elements should be constructed.  Each
 * `pg_state' has a set of name format strings, one for each class or kind of
 * presentation element.  The set of presentation element types is listed in
 * `mom/c/pfe.hh'.  The set of `%'-escapes interpreted by `calc_name' is listed
 * in the code below.
 *
 * `calc_name' is almost never called directly.  Rather, it is called through
 * helper methods that are each specific to one kind of element.  For example,
 * to compute the name of a client stub, a presentation generator should call
 * `calc_client_stub_name' rather than `calc_name'.  `calc_client_stub_name'
 * locates the format string for client stub names and passes it to `calc_name'
 * along with the AOI-defined (unscoped) name of the operation that will be
 * implemented.
 *
 * Because all of the name format strings are collected in one place within the
 * `pg_state' class, it is easy for individual presentation generators to
 * change the ``rules'' for building presented names.  It is also possible to
 * make a command-line option corresponding to each format string, so that
 * users can customize presentations to some degree.
 *
 * Because there is a method for each kind of presentation element, it is
 * possible for a presentation generator to customize name generation by
 * overriding specific `calc_*_name' methods.  This technique should be used
 * ONLY when the `calc_name' method cannot implement the required name
 * construction rules.  (Method overrides reduce users' ability to customize
 * name generation through the command line options describe above.)
 */


/*****************************************************************************/

/* Old cruft that still lingers... */
void pg_state::init_name_context()
{
	name = pg_state_name_strlit("");
}


/*****************************************************************************/

/*
 * We allow users to change the `pg_state' name format strings and literal
 * strings through command line options.  This facility will become obsolete
 * and go away when we implement a ``real'' presentation modification facility.
 *
 * The macro `DEFINE_CALC_NAME_FMT_OPTION' creates a `name_fmt_option_struct'
 * corresponding to a format string within a `pg_state'.  Similarly, the macro
 * `DEFINE_CALC_NAME_LIT_OPTION' creates a `name_lit_option_struct'
 * corresponding to a literal string.  The function `pg_state::build_flags' in
 * `pg_args.cc' scans the arrays of name format and literal options defined
 * below in order to create and execute the ``real'' command line option
 * structures.
 *
 * For both macros, `type' is the name of a format/literal string kind, minus
 * the `_fmt'/`_lit' extension, as defined in `mom/c/pfe.hh'.  `explain' is a
 * human-readable description of the type of elements to which the format is
 * applied (e.g., client stubs, object types, etc.), or a description of the
 * purpose of the literal string.  Let the examples below guide you.
 */

#define DEFINE_CALC_NAME_FMT_OPTION(type, explain)	\
{							\
	#type "_fmt",					\
	name_strings::type##_fmt,			\
	"Specify the format of " explain		\
}

#define DEFINE_CALC_NAME_LIT_OPTION(type, explain)	\
{							\
	#type "_lit",					\
	name_strings::type##_lit,			\
	"Specify the string for " explain		\
}

name_fmt_option_struct name_fmt_options[] =
{
	/* The `null' format should never be used.       */
	/* DEFINE_CALC_NAME_FMT_OPTION(null, "nothing"), */
	
	DEFINE_CALC_NAME_FMT_OPTION(client_stub,
				    "client stub names"),
	
	DEFINE_CALC_NAME_FMT_OPTION(server_skel,
				    "server skeleton names"),
	DEFINE_CALC_NAME_FMT_OPTION(client_skel,
				    "client skeleton names"),
	DEFINE_CALC_NAME_FMT_OPTION(server_func,
				    "server function names"),
	DEFINE_CALC_NAME_FMT_OPTION(receive_request_func,
				    "request receive function names"),
	DEFINE_CALC_NAME_FMT_OPTION(receive_reply_func,
				    "reply receive function names"),
	
	DEFINE_CALC_NAME_FMT_OPTION(marshal_stub,
				    "marshal stub names"),
	DEFINE_CALC_NAME_FMT_OPTION(unmarshal_stub,
				    "unmarshal stub names"),
	
	DEFINE_CALC_NAME_FMT_OPTION(message_marshal_request_stub,
				    "message marshal request stub names"),
	DEFINE_CALC_NAME_FMT_OPTION(message_unmarshal_request_stub,
				    "message unmarshal request stub names"),
	DEFINE_CALC_NAME_FMT_OPTION(message_marshal_reply_stub,
				    "message marshal reply stub names"),
	DEFINE_CALC_NAME_FMT_OPTION(message_marshal_except_stub,
				    "message marshal exception stub names"),
	DEFINE_CALC_NAME_FMT_OPTION(message_unmarshal_reply_stub,
				    "message unmarshal reply stub names"),
	
	DEFINE_CALC_NAME_FMT_OPTION(message_request_type,
				    "message request type names"),
	DEFINE_CALC_NAME_FMT_OPTION(message_reply_type,
				    "message reply type names"),
	DEFINE_CALC_NAME_FMT_OPTION(interface_message_request_type,
				    "message request type names"),
	DEFINE_CALC_NAME_FMT_OPTION(interface_message_reply_type,
				    "message reply type names"),
	
	DEFINE_CALC_NAME_FMT_OPTION(send_request_stub,
				    "send request stub names"),
	DEFINE_CALC_NAME_FMT_OPTION(send_reply_stub,
				    "send reply stub names"),
	DEFINE_CALC_NAME_FMT_OPTION(recv_request_stub,
				    "receive request stub names"),
	DEFINE_CALC_NAME_FMT_OPTION(recv_reply_stub,
				    "receive reply stub names"),
	
	DEFINE_CALC_NAME_FMT_OPTION(continue_request_stub,
				    "request continuation stub names"),
	DEFINE_CALC_NAME_FMT_OPTION(continue_reply_stub,
				    "reply continuation stub names"),
	DEFINE_CALC_NAME_FMT_OPTION(request_continuer_func_type,
				    "request continuation func type names"),
	DEFINE_CALC_NAME_FMT_OPTION(reply_continuer_func_type,
				    "reply continuation func type names"),
	
	DEFINE_CALC_NAME_FMT_OPTION(const,
				    "constant names"),
	DEFINE_CALC_NAME_FMT_OPTION(type,
				    "ordinary data type names"),
	
	DEFINE_CALC_NAME_FMT_OPTION(enum_tag,
				    "enumeration type tags/labels"),
	DEFINE_CALC_NAME_FMT_OPTION(enum_member,
				    "enumeration member names"),
	
	DEFINE_CALC_NAME_FMT_OPTION(struct_slot,
				    "structure slot names"),
	DEFINE_CALC_NAME_FMT_OPTION(struct_union_tag,
				    "tags of unions within discriminated "
				    "union types"),
	
	DEFINE_CALC_NAME_FMT_OPTION(basic_message_type,
				    "``generic'' (asynchronous) "
				    "message object types"),
	DEFINE_CALC_NAME_FMT_OPTION(client_basic_object_type,
				    "``generic'' client object types"),
	DEFINE_CALC_NAME_FMT_OPTION(server_basic_object_type,
				    "``generic'' server object types"),
	
	DEFINE_CALC_NAME_FMT_OPTION(client_interface_object_type,
				    "interface-specific client object types"),
	DEFINE_CALC_NAME_FMT_OPTION(server_interface_object_type,
				    "interface-specific server object types"),
	
	DEFINE_CALC_NAME_FMT_OPTION(client_stub_object_type,
				    "client stub target object types"),
	DEFINE_CALC_NAME_FMT_OPTION(server_func_object_type,
				    "server function target object types"),
	
	DEFINE_CALC_NAME_FMT_OPTION(client_stub_environment_type,
				    "client stub environment types"),
	DEFINE_CALC_NAME_FMT_OPTION(server_func_environment_type,
				    "server function environment types"),
	
	DEFINE_CALC_NAME_FMT_OPTION(stub_invocation_id_type,
				    "send/receive stub invocation id type"),
	DEFINE_CALC_NAME_FMT_OPTION(stub_client_ref_type,
				    "send/receive stub client reference type"),
	
	DEFINE_CALC_NAME_FMT_OPTION(client_stub_client_sid_type,
				    "client stub client SID types"),
	DEFINE_CALC_NAME_FMT_OPTION(server_func_client_sid_type,
				    "server function client SID types"),
	
	DEFINE_CALC_NAME_FMT_OPTION(client_stub_server_sid_type,
				    "client stub server SID types"),
	DEFINE_CALC_NAME_FMT_OPTION(server_func_server_sid_type,
				    "server function server SID types"),
	
	DEFINE_CALC_NAME_FMT_OPTION(stub_param,
				    "stub data parameter names"),
	
	DEFINE_CALC_NAME_FMT_OPTION(client_stub_object_param,
				    "client stub target object parameter "
				    "names"),
	DEFINE_CALC_NAME_FMT_OPTION(server_func_object_param,
				    "server function target object parameter "
				    "names"),
	
	DEFINE_CALC_NAME_FMT_OPTION(client_stub_environment_param,
				    "client stub environment parameter names"),
	DEFINE_CALC_NAME_FMT_OPTION(server_func_environment_param,
				    "server function environment parameter "
				    "names"),
	
	DEFINE_CALC_NAME_FMT_OPTION(client_stub_client_sid_param,
				    "client stub client SID parameter names"),
	DEFINE_CALC_NAME_FMT_OPTION(server_func_client_sid_param,
				    "server function client SID parameter "
				    "names"),
	
	DEFINE_CALC_NAME_FMT_OPTION(client_stub_required_server_sid_param,
				    "client stub required server SID "
				    "parameter names"),
	DEFINE_CALC_NAME_FMT_OPTION(server_func_required_server_sid_param,
				    "server function required server SID "
				    "parameter names"),
	
	DEFINE_CALC_NAME_FMT_OPTION(client_stub_actual_server_sid_param,
				    "client stub actual server SID parameter "
				    "names"),
	DEFINE_CALC_NAME_FMT_OPTION(server_func_actual_server_sid_param,
				    "server function actual server SID "
				    "parameter names"),
	
	DEFINE_CALC_NAME_FMT_OPTION(operation_request_code,
				    "operation request codes"),
	DEFINE_CALC_NAME_FMT_OPTION(operation_reply_code,
				    "operation reply codes"),
	
	DEFINE_CALC_NAME_FMT_OPTION(exception_type,
				    "exception type names"),
	DEFINE_CALC_NAME_FMT_OPTION(exception_code,
				    "exception codes"),
	
	DEFINE_CALC_NAME_FMT_OPTION(allocator_function,
				    "memory allocation functions"),
	DEFINE_CALC_NAME_FMT_OPTION(deallocator_function,
				    "memory deallocation functions"),
	
	DEFINE_CALC_NAME_FMT_OPTION(presentation_include_file,
				    "the presentation style `#include' file "
				    "name"),
	DEFINE_CALC_NAME_FMT_OPTION(interface_include_file,
				    "per-interface `#include' file names"),
	DEFINE_CALC_NAME_FMT_OPTION(interface_default_include_file,
				    "default per-interface `#include' file "
				    "names"),
	
	/* `pg_state::build_flags' looks for a terminating null entry. */
	{ 0, name_strings::null_fmt, 0}
};

name_lit_option_struct name_lit_options[] =
{
	/* The `null' literal should never be used.      */
	/* DEFINE_CALC_NAME_LIT_OPTION(null, "nothing"), */
	
	DEFINE_CALC_NAME_LIT_OPTION(separator,
				    "name component separators (`%_')"),
	
	DEFINE_CALC_NAME_LIT_OPTION(presentation_style,
				    "the presentation style (`%g')"),
	
	DEFINE_CALC_NAME_LIT_OPTION(filename_component_separator,
				    "filename component separators (`%/')"),
	
	/* `pg_state::build_flags' looks for a terminating null entry. */
	{ 0, name_strings::null_lit, 0}
};


/*****************************************************************************/

/*
 * In order to allow fine-grained control over name generation in different
 * presentation generators, we define a `pg_state' member function for every
 * kind of name that we generate.  That is, we define one member function for
 * each kind of name format string.  The basic `pg_state' implementation of
 * each member function is simple: just call `calc_name' with the format string
 * for the given presentation element type.
 *
 * Individual presentation generators then have two ways to change how the
 * names for specific presentation elements are built.  The first (preferred)
 * method is to change the default value of the format string corresponding to
 * the presentation element type.  The second technique is to override the
 * method that is called to generate names for that presentation element type.
 * The latter method may be used when the existing set of `calc_name' format
 * escapes is insufficient to describe the names that must be created.
 *
 * The macro `DEFINE_CALC_NAME_FUNCTION' is used to define the base `pg_state'
 * methods for each type of presentation element.
 */

#define DEFINE_CALC_NAME_FUNCTION(type)					\
  char *pg_state::calc_##type##_name(const char *basic_name)		\
  {									\
	return calc_name(names.formats[name_strings::type##_fmt],	\
			 basic_name);					\
  }

#define DEFINE_CALC_SCOPED_NAME_FUNCTION(type)				    \
  cast_scoped_name pg_state::calc_##type##_scoped_name(			    \
	aoi_ref parent_ref,						    \
	const char *base_name)						    \
  {									    \
	cast_scoped_name scname = null_scope_name;			    \
									    \
	calc_scoped_name(&scname,					    \
			 parent_ref,					    \
			 names.formats[name_strings::type##_scoped_fmt]); \
	cast_add_scope_name(&scname,					    \
			    base_name,					    \
			    null_template_arg_array);			    \
	return scname;							    \
  }

/* The `null' format should never be used. */
/* DEFINE_CALC_NAME_FUNCTION(null)         */

DEFINE_CALC_NAME_FUNCTION(client_stub)

DEFINE_CALC_SCOPED_NAME_FUNCTION(client_stub)

DEFINE_CALC_NAME_FUNCTION(server_skel)
DEFINE_CALC_NAME_FUNCTION(client_skel)
DEFINE_CALC_NAME_FUNCTION(server_func)
DEFINE_CALC_NAME_FUNCTION(receive_request_func)
DEFINE_CALC_NAME_FUNCTION(receive_reply_func)

DEFINE_CALC_SCOPED_NAME_FUNCTION(server_skel)
DEFINE_CALC_SCOPED_NAME_FUNCTION(client_skel)
DEFINE_CALC_SCOPED_NAME_FUNCTION(server_func)
DEFINE_CALC_SCOPED_NAME_FUNCTION(receive_request_func)
DEFINE_CALC_SCOPED_NAME_FUNCTION(receive_reply_func)

DEFINE_CALC_NAME_FUNCTION(marshal_stub)
DEFINE_CALC_NAME_FUNCTION(unmarshal_stub)

DEFINE_CALC_SCOPED_NAME_FUNCTION(marshal_stub)
DEFINE_CALC_SCOPED_NAME_FUNCTION(unmarshal_stub)

DEFINE_CALC_NAME_FUNCTION(message_marshal_request_stub)
DEFINE_CALC_NAME_FUNCTION(message_unmarshal_request_stub)
DEFINE_CALC_NAME_FUNCTION(message_marshal_reply_stub)
DEFINE_CALC_NAME_FUNCTION(message_marshal_except_stub)
DEFINE_CALC_NAME_FUNCTION(message_unmarshal_reply_stub)

DEFINE_CALC_SCOPED_NAME_FUNCTION(message_marshal_request_stub)
DEFINE_CALC_SCOPED_NAME_FUNCTION(message_unmarshal_request_stub)
DEFINE_CALC_SCOPED_NAME_FUNCTION(message_marshal_reply_stub)
DEFINE_CALC_SCOPED_NAME_FUNCTION(message_marshal_except_stub)
DEFINE_CALC_SCOPED_NAME_FUNCTION(message_unmarshal_reply_stub)

DEFINE_CALC_NAME_FUNCTION(message_request_type)
DEFINE_CALC_NAME_FUNCTION(message_reply_type)
DEFINE_CALC_NAME_FUNCTION(interface_message_request_type)
DEFINE_CALC_NAME_FUNCTION(interface_message_reply_type)

DEFINE_CALC_SCOPED_NAME_FUNCTION(message_request_type)
DEFINE_CALC_SCOPED_NAME_FUNCTION(message_reply_type)
DEFINE_CALC_SCOPED_NAME_FUNCTION(interface_message_request_type)
DEFINE_CALC_SCOPED_NAME_FUNCTION(interface_message_reply_type)

DEFINE_CALC_NAME_FUNCTION(send_request_stub)
DEFINE_CALC_NAME_FUNCTION(send_reply_stub)
DEFINE_CALC_NAME_FUNCTION(recv_request_stub)
DEFINE_CALC_NAME_FUNCTION(recv_reply_stub)

DEFINE_CALC_SCOPED_NAME_FUNCTION(send_request_stub)
DEFINE_CALC_SCOPED_NAME_FUNCTION(send_reply_stub)
DEFINE_CALC_SCOPED_NAME_FUNCTION(recv_request_stub)
DEFINE_CALC_SCOPED_NAME_FUNCTION(recv_reply_stub)

DEFINE_CALC_NAME_FUNCTION(continue_request_stub)
DEFINE_CALC_NAME_FUNCTION(continue_reply_stub)
DEFINE_CALC_NAME_FUNCTION(request_continuer_func_type)
DEFINE_CALC_NAME_FUNCTION(reply_continuer_func_type)

DEFINE_CALC_SCOPED_NAME_FUNCTION(continue_request_stub)
DEFINE_CALC_SCOPED_NAME_FUNCTION(continue_reply_stub)
DEFINE_CALC_SCOPED_NAME_FUNCTION(request_continuer_func_type)
DEFINE_CALC_SCOPED_NAME_FUNCTION(reply_continuer_func_type)

DEFINE_CALC_NAME_FUNCTION(const)
DEFINE_CALC_NAME_FUNCTION(type)

DEFINE_CALC_SCOPED_NAME_FUNCTION(const)
DEFINE_CALC_SCOPED_NAME_FUNCTION(type)

DEFINE_CALC_NAME_FUNCTION(enum_tag)
DEFINE_CALC_NAME_FUNCTION(enum_member)

DEFINE_CALC_SCOPED_NAME_FUNCTION(enum_tag)

DEFINE_CALC_NAME_FUNCTION(struct_slot)
DEFINE_CALC_NAME_FUNCTION(struct_union_tag)

DEFINE_CALC_NAME_FUNCTION(basic_message_type)
DEFINE_CALC_NAME_FUNCTION(client_basic_object_type)
DEFINE_CALC_NAME_FUNCTION(server_basic_object_type)

DEFINE_CALC_SCOPED_NAME_FUNCTION(basic_message_type)
DEFINE_CALC_SCOPED_NAME_FUNCTION(client_basic_object_type)
DEFINE_CALC_SCOPED_NAME_FUNCTION(server_basic_object_type)

DEFINE_CALC_NAME_FUNCTION(client_interface_object_type)
DEFINE_CALC_NAME_FUNCTION(server_interface_object_type)

DEFINE_CALC_SCOPED_NAME_FUNCTION(client_interface_object_type)
DEFINE_CALC_SCOPED_NAME_FUNCTION(server_interface_object_type)

DEFINE_CALC_NAME_FUNCTION(client_stub_object_type)
DEFINE_CALC_NAME_FUNCTION(server_func_object_type)

DEFINE_CALC_SCOPED_NAME_FUNCTION(client_stub_object_type)
DEFINE_CALC_SCOPED_NAME_FUNCTION(server_func_object_type)

DEFINE_CALC_NAME_FUNCTION(client_stub_environment_type)
DEFINE_CALC_NAME_FUNCTION(server_func_environment_type)

DEFINE_CALC_SCOPED_NAME_FUNCTION(client_stub_environment_type)
DEFINE_CALC_SCOPED_NAME_FUNCTION(server_func_environment_type)

DEFINE_CALC_NAME_FUNCTION(stub_invocation_id_type)
DEFINE_CALC_NAME_FUNCTION(stub_client_ref_type)

DEFINE_CALC_SCOPED_NAME_FUNCTION(stub_invocation_id_type)
DEFINE_CALC_SCOPED_NAME_FUNCTION(stub_client_ref_type)

DEFINE_CALC_NAME_FUNCTION(client_stub_client_sid_type)
DEFINE_CALC_NAME_FUNCTION(server_func_client_sid_type)

DEFINE_CALC_SCOPED_NAME_FUNCTION(client_stub_client_sid_type)
DEFINE_CALC_SCOPED_NAME_FUNCTION(server_func_client_sid_type)

DEFINE_CALC_NAME_FUNCTION(client_stub_server_sid_type)
DEFINE_CALC_NAME_FUNCTION(server_func_server_sid_type)

DEFINE_CALC_SCOPED_NAME_FUNCTION(client_stub_server_sid_type)
DEFINE_CALC_SCOPED_NAME_FUNCTION(server_func_server_sid_type)

DEFINE_CALC_NAME_FUNCTION(stub_param)

DEFINE_CALC_NAME_FUNCTION(client_stub_object_param)
DEFINE_CALC_NAME_FUNCTION(server_func_object_param)

DEFINE_CALC_NAME_FUNCTION(client_stub_environment_param)
DEFINE_CALC_NAME_FUNCTION(server_func_environment_param)

DEFINE_CALC_NAME_FUNCTION(client_stub_client_sid_param)
DEFINE_CALC_NAME_FUNCTION(server_func_client_sid_param)

DEFINE_CALC_NAME_FUNCTION(client_stub_required_server_sid_param)
DEFINE_CALC_NAME_FUNCTION(server_func_required_server_sid_param)

DEFINE_CALC_NAME_FUNCTION(client_stub_actual_server_sid_param)
DEFINE_CALC_NAME_FUNCTION(server_func_actual_server_sid_param)

DEFINE_CALC_NAME_FUNCTION(operation_request_code)
DEFINE_CALC_NAME_FUNCTION(operation_reply_code)

DEFINE_CALC_NAME_FUNCTION(exception_type)
DEFINE_CALC_NAME_FUNCTION(exception_code)

DEFINE_CALC_SCOPED_NAME_FUNCTION(exception_type)

DEFINE_CALC_NAME_FUNCTION(allocator_function)
DEFINE_CALC_NAME_FUNCTION(deallocator_function)

DEFINE_CALC_SCOPED_NAME_FUNCTION(allocator_function)
DEFINE_CALC_SCOPED_NAME_FUNCTION(deallocator_function)

DEFINE_CALC_NAME_FUNCTION(presentation_include_file)
DEFINE_CALC_NAME_FUNCTION(interface_include_file)
DEFINE_CALC_NAME_FUNCTION(interface_default_include_file)

/*
 * This function is another useful interface to `calc_name': compute the name
 * of an presentation element from an AOI reference (i.e., an index to an AOI
 * definition).  Of course, this function is only useful for objects that get
 * their names from `aoi_def's: generally, types and interface references.
 *
 * See how this function is used by `p_typedef_def' and `p_make_ctypename'.
 */
char *pg_state::calc_name_from_ref(aoi_ref ref)
{
	aoi_ref saved_cur_aoi_idx;
	aoi_ref saved_derived_interface_ref;
	aoi_ref saved_parent_interface_ref;
	
	aoi_kind ref_kind;
	char *ref_name;
	
	/*****/
	
	assert((ref >= 0) && (ref < ((aoi_ref) in_aoi->defs.defs_len)));
	
	ref_kind = a(ref).binding->kind;
	ref_name = a(ref).name;
	
	/*
	 * Set `cur_aoi_idx' to `ref' so that `calc_name' can find the right
	 * scope information.
	 *
	 * Moreover, set our interface references to `aoi_ref_null' so that we
	 * don't inadvertently generate names as if they were defined as part
	 * of the interfaces we might be processing at the moment.  `ref' *may*
	 * refer to something that is within the current interface, but any
	 * scope components in the name generated for `ref' will have to be
	 * derived from `ref' itself, not from the fact that we happen to be
	 * processing any particluar interface at the moment.  In short, we
	 * need to generate a globally appropriate name for `ref', not a name
	 * driven by the PG's current context.
	 */
	saved_cur_aoi_idx = cur_aoi_idx;
	saved_derived_interface_ref = derived_interface_ref;
	saved_parent_interface_ref = parent_interface_ref;
	
	cur_aoi_idx = ref;
	derived_interface_ref = aoi_ref_null;
	parent_interface_ref = aoi_ref_null;
	
	/*
	 * Generate the name by invoking the `calc_*_name' function that is
	 * appropriate for `ref_kind'.
	 */
	switch (ref_kind) {
	default:
		ref_name = calc_type_name(ref_name);
		break;
		
	case AOI_INTERFACE:
	case AOI_FWD_INTRFC:
		if (gen_client)
			ref_name = calc_client_interface_object_type_name(
				ref_name);
		else if (gen_server)
			ref_name = calc_server_interface_object_type_name(
				ref_name);
		else
			panic("In `pg_state::calc_name_from_ref', "
			      "generating neither client nor server.");
		break;
		
	case AOI_EXCEPTION:
		ref_name = calc_exception_type_name(ref_name);
		break;
	}
	
	/* Restore our AOI references before returning. */
	cur_aoi_idx = saved_cur_aoi_idx;
	derived_interface_ref = saved_derived_interface_ref;
	parent_interface_ref = saved_parent_interface_ref;

	return ref_name;
}

cast_scoped_name pg_state::calc_scoped_name_from_ref(aoi_ref ref)
{
	aoi_ref saved_cur_aoi_idx;
	aoi_ref saved_derived_interface_ref;
	aoi_ref saved_parent_interface_ref;
	
	aoi_kind ref_kind;
	char *ref_name;
	cast_scoped_name scoped_name = null_scope_name;
	
	/*****/
	
	assert((ref >= 0) && (ref < ((aoi_ref) in_aoi->defs.defs_len)));
	
	ref_kind = a(ref).binding->kind;
	ref_name = a(ref).name;
	
	/*
	 * Set `cur_aoi_idx' to `ref' so that `calc_name' can find the right
	 * scope information.
	 *
	 * Moreover, set our interface references to `aoi_ref_null' so that we
	 * don't inadvertently generate names as if they were defined as part
	 * of the interfaces we might be processing at the moment.  `ref' *may*
	 * refer to something that is within the current interface, but any
	 * scope components in the name generated for `ref' will have to be
	 * derived from `ref' itself, not from the fact that we happen to be
	 * processing any particluar interface at the moment.  In short, we
	 * need to generate a globally appropriate name for `ref', not a name
	 * driven by the PG's current context.
	 */
	saved_cur_aoi_idx = cur_aoi_idx;
	saved_derived_interface_ref = derived_interface_ref;
	saved_parent_interface_ref = parent_interface_ref;
	
	cur_aoi_idx = ref;
	derived_interface_ref = aoi_ref_null;
	parent_interface_ref = aoi_ref_null;

	ref = aoi_get_parent_scope(in_aoi, ref);
	/*
	 * Generate the name by invoking the `calc_*_name' function that is
	 * appropriate for `ref_kind'.
	 */
	switch (ref_kind) {
	default:
		scoped_name = calc_type_scoped_name(
			ref,
			calc_type_name(ref_name)
			);
		break;
		
	case AOI_INTERFACE:
	case AOI_FWD_INTRFC:
		if (gen_client)
			scoped_name =
				calc_client_interface_object_type_scoped_name(
					ref,
					calc_client_interface_object_type_name(
						ref_name)
					);
		else if (gen_server)
			scoped_name =
				calc_server_interface_object_type_scoped_name(
					ref,
					calc_server_interface_object_type_name(
						ref_name)
					);
		else
			panic("In `pg_state::calc_name_from_ref', "
			      "generating neither client nor server.");
		break;
		
	case AOI_EXCEPTION:
		scoped_name = calc_exception_type_scoped_name(
			ref,
			calc_exception_type_name(ref_name)
			);
		break;
	}
	
	/* Restore our AOI references before returning. */
	cur_aoi_idx = saved_cur_aoi_idx;
	derived_interface_ref = saved_derived_interface_ref;
	parent_interface_ref = saved_parent_interface_ref;

	return scoped_name;
}


/*****************************************************************************/

/*
 * Here is the implementation of the core name-calcuation engine.
 *
 * First, define a macro for adding a name compoenent to our internal list
 * (array) of components, reallocating the array as necessary.
 */

#define CALC_NAME_COMPONENTS_INCREMENT (16)

#define CALC_NAME_ADD_COMPONENT(data, data_len)     		 \
	do {								 \
		if (calc_name_data.count >= calc_name_data.size) {	 \
			calc_name_data.size +=				 \
				CALC_NAME_COMPONENTS_INCREMENT;		 \
			calc_name_data.components =			 \
				(calc_name_component *)			 \
				mustrealloc(calc_name_data.components,	 \
					    (sizeof(calc_name_component) \
					     * calc_name_data.size));	 \
		}							 \
									 \
		calc_name_data.components[calc_name_data.count].str	 \
			= (data); \
		calc_name_data.components[calc_name_data.count].len \
			= (data_len); \
									 \
		++calc_name_data.count;					 \
	} while (0)

#define NAME_LITERAL_STR(type) names.literals[name_strings::type##_lit].str
#define NAME_LITERAL_LEN(type) names.literals[name_strings::type##_lit].len

/*
 * An internal auxiliary.  `calc_name_module' locates the names of the AOI
 * objects (modules, interfaces, or types) that contain the AOI object
 * referenced by `last_ref'.  These names, along with appropriate separators,
 * are added to our list of name pieces.
 *
 * `last_ref' is the initial AOI reference.  This function does *not* add the
 * name associated with `last_ref' to our list of name components.
 *
 * If `qualified_p' is true, `calc_name_module' traverses the AOI scopes all
 * the way back to the root scope.  The name of every encompassing object is
 * added to our name component list.  If `qualified_p' is false, then only the
 * name of the directly encompassing object is added to our name list.
 *
 * If `separator_p' is true, then we output a separator immediately after the
 * output name component.  This flag is set for all recursive calls.
 */

void pg_state::calc_name_module(aoi_ref last_ref,
				int qualified_p,
				int separator_p)
{
	aoi_ref ref;
	
	if( a(last_ref).scope <= 0 )
		return;
	
	ref = aoi_get_parent_scope(in_aoi, last_ref);
	
	assert(a(ref).binding);
	/*
	 * XXX --- Relax this assertion because now we call this function with
	 * things that aren't interfaces, but which may be conatined in
	 * interfaces (e.g., type names).
	 *
	 * assert(a(ref).binding->kind == AOI_NAMESPACE);
	 *
	 * XXX --- Relax it a lot.  Structs define scopes, too!
	 */
#if 0
	assert((a(ref).binding->kind == AOI_NAMESPACE)
	       || (a(ref).binding->kind == AOI_INTERFACE));
#endif
	
	/* Output the names of the scopes conatining `ref'. */
	if (qualified_p == CALC_NAME_QUALIFIED)
		calc_name_module(ref,
				 CALC_NAME_QUALIFIED, CALC_NAME_SEPARATOR);
	
	/* Now output the name of this scope. */
	CALC_NAME_ADD_COMPONENT(a(ref).name, strlen(a(ref).name));
	if (separator_p == CALC_NAME_SEPARATOR)
		CALC_NAME_ADD_COMPONENT(NAME_LITERAL_STR(separator),
					NAME_LITERAL_LEN(separator));
}

/*
 * Finally, the core of the name generation engine.  `calc_name' interprets a
 * `printf'-like format string in order to construct the name of a presentation
 * element.  The current set of `%'-escapes is this:
 *
 * `%_'		A name component separator (as determined by `names.literals').
 * `%-'		Same as `%_'.
 * `%/'		A filename component separator.
 *
 * `%g'		The name of the presentation style (from `names.literals').
 *
 * `%s'		The unscoped name of the element, generally taken from the IDL
 *		input.  This string is found in the `object_name' parameter.
 *
 * `%S'		The scoped name of the element, with separators between the
 *		components.
 *
 * `%m'		The unscoped name of the module/interface/... element in which
 *		the current-being-defined element is contained (i.e., its
 *		``lexical parent'').
 *
 * `%M'		The scoped name of the element in which the current element is
 *		contained (i.e., its ``lexical lineage'').
 *
 * `%i'		The unscoped name of the interface in which the current element
 *		is being defined.  If the current element is not a component of
 *		an interface, `%i' produces nothing.
 *
 * `%I'		The scoped name of the interface in which the current element
 *		is being defined.  As with `%i', this produces nothing if the
 *		current element is not part of an interface.
 *
 * `%p'		The unscoped name of the interface from which the current
 *		interface element is being inherited (`p' for `parent').  If
 *		the element is being defined by the current interface, `%p' is
 *		the same as `%i'.  If the current element is not part of an
 *		interface, `%p' produces nothing.
 *
 * `%P'		The scoped name of the interface from which the current
 *		interface element is being inherited.
 *
 * `%n'		The unscoped name of the module/interface/... that contains the
 *		interface from which the current interface element in being
 *		derived.  (`%n' is `%m' applied to the ``parent interface.'')
 *		I haven't figured out why this would be useful, and `n' is a
 *		terrible mnemonic for this anyway.
 *
 * `%N'		The scoped version of `%n'.
 *
 * To interpret these escapes, `calc_name' refers to three AOI references kept
 * in the invoking `pg_state':
 *
 * `cur_aoi_idx'		The current top-level AOI definition.
 * `derived_interface_ref'	The AOI interface that is currently being
 *				processed (by `p_client_stubs/p_server_skel').
 * `parent_interface_ref'	The AOI interface from which the current
 *				interface (`derived_interface_ref') is
 *				inheriting a component operation, type, ...
 *
 * XXX --- I am not sure that `calc_name' properly handles all of the ways in
 * which these AOI references may interact with one another.  `cur_aoi_idx' was
 * sort of bashed in at the last moment.  Look for `was derived_ref' and XXX in
 * the code below.
 *
 * XXX --- Ideas for new escapes:
 *
 * `%c'		The ``code'' of this element (e.g., an interface) as specified
 *		in the AOI file.  This would perhaps require a PG-specific code
 *		extraction method.  Adding `%c' would help eliminate the Sun
 *		PG overrides of certain `calc_*_name' methods.
 *
 * `%('		Groups of alternatives.  Process the alternatives until one
 * `%)'		results in a non-empty string.  This would help eliminate the
 * `%|'		`interface_default_include_file' format: the Fluke PG could set
 *		the `interface_include_file' format string to something like
 *		`fluke%/%(%M%|flick%).h'.
 */

char *pg_state::calc_name(const char *format, const char *object_name)
{
	aoi_ref parent_ref = parent_interface_ref;
	aoi_ref derived_ref = derived_interface_ref;
	aoi_ref current_ref = cur_aoi_idx;
	
	char *result_str;
	int result_len;
	
	const char *src;
	char *dst;
	int len;
	
	int i;
	
	/*
	 * Parse the format string into a sequence of name components.
	 */
	calc_name_data.count = 0;
	
	for (src = format; *src; ) {
		if (*src != '%') {
			/*
			 * Simple case: Copy characters from input to output.
			 */
			for (len = 0; (src[len] && (src[len] != '%')); ++len)
				/* Do nothing. */ ;
			CALC_NAME_ADD_COMPONENT(src, len);
			src += len;
			
			/*
			 * We could `continue' at this point, but let's be a
			 * little more clever instead.  `*src' is either a `%'
			 * or a NUL: if it's `%' we can fall through to the
			 * `switch' below.
			 */
			if (!(*src))
				break;
		}
		
		/*
		 * Hard cases: Expand a `%' macro in the format string.
		 */
		switch (*(++src)) {
		default:
			panic("In `pg_state::calc_name', "
			      "unknown dispatch macro `%%%c'.", *src);
			break;
			
			/*
			 * Literal strings: separators, ...
			 */
		case '-':
		case '_':
			CALC_NAME_ADD_COMPONENT(
				NAME_LITERAL_STR(separator),
				NAME_LITERAL_LEN(separator)
				);
			break;
		case '/':
			CALC_NAME_ADD_COMPONENT(
				NAME_LITERAL_STR(filename_component_separator),
				NAME_LITERAL_LEN(filename_component_separator)
				);
			break;
		case 'g':
			CALC_NAME_ADD_COMPONENT(
				NAME_LITERAL_STR(presentation_style),
				NAME_LITERAL_LEN(presentation_style));
			break;
			
			/*
			 * Module names.
			 */
		case 'M':
			calc_name_module(current_ref /* was `derived_ref' */,
					 CALC_NAME_QUALIFIED,
					 CALC_NAME_NO_SEPARATOR);
			break;
			
		case 'm':
			calc_name_module(current_ref /* was `derived_ref' */,
					 CALC_NAME_QUALIFIED,
					 CALC_NAME_NO_SEPARATOR);
			break;
			
		case 'N':
			calc_name_module(parent_ref,
					 CALC_NAME_QUALIFIED,
					 CALC_NAME_NO_SEPARATOR);
			break;
			
		case 'n':
			calc_name_module(parent_ref,
					 CALC_NAME_QUALIFIED,
					 CALC_NAME_NO_SEPARATOR);
			break;
			
			/*
			 * Parent interface names.
			 */
		case 'P':
			calc_name_module(parent_ref,
					 CALC_NAME_QUALIFIED,
					 CALC_NAME_SEPARATOR);
			/* FALLTHROUGH */
		case 'p':
			if (parent_ref != aoi_ref_null)
				CALC_NAME_ADD_COMPONENT(a(parent_ref).name,
							strlen(a(parent_ref).
							       name));
			break;
			
			/*
			 * Derived interface names.
			 */
		case 'I':
			calc_name_module(current_ref /* was `derived_ref' */,
					 CALC_NAME_QUALIFIED,
					 CALC_NAME_SEPARATOR);
			/* XXX? --- Is this next `if' right? */
			if (current_ref != derived_ref)
				break;
			/* FALLTHROUGH */
		case 'i':
			if (derived_ref != aoi_ref_null)
				CALC_NAME_ADD_COMPONENT(a(derived_ref).name,
							strlen(a(derived_ref).
							       name));
			break;
			
			/*
			 * The name of the current interface component:
			 * operation, attribute, type, whatever.
			 */
		case 'S':
			calc_name_module(current_ref /* was `derived_ref' */,
					 CALC_NAME_QUALIFIED,
					 CALC_NAME_SEPARATOR);
			/* XXX? --- Is this next `if' right? */
			if (current_ref != derived_ref)
				;
			else if (derived_ref != aoi_ref_null) {
				CALC_NAME_ADD_COMPONENT(a(derived_ref).name,
							strlen(a(derived_ref).
							       name));
				CALC_NAME_ADD_COMPONENT(
					NAME_LITERAL_STR(separator),
					NAME_LITERAL_LEN(separator));
			}
			/* FALLTHROUGH. */
		case 's':
			CALC_NAME_ADD_COMPONENT(object_name,
						strlen(object_name));
			break;
		}
		++src;
	}
	
	/*
	 * Concatenate all of the components into the final name string.
	 */
	result_len = 1; /* 1 for terminating NUL. */
	for (i = 0; i < calc_name_data.count; ++i)
		result_len += calc_name_data.components[i].len;
	
	result_str = (char *) mustmalloc(sizeof(char) * result_len);
	
	for (i = 0, dst = result_str; i < calc_name_data.count; ++i) {
		strncpy(dst,
			calc_name_data.components[i].str,
			calc_name_data.components[i].len);
		dst += calc_name_data.components[i].len;
	}
	*dst = 0;
	
	// fprintf(stderr, "  In:    `%s' `%s'\n", format, object_name);
	// fprintf(stderr, "    Out: `%s'\n", result_str);
	return result_str;
}

void pg_state::calc_scoped_name(cast_scoped_name *scoped_name,
				aoi_ref ref,
				const char *format)
{
	char *result_str;
	int result_len;
	
	const char *src;
	char *dst;
	int len;
	
	int i;

	if( !format || !(*format) || (ref == aoi_ref_null) ) {
		if( scoped_name->cast_scoped_name_len > 1 )
			cast_prepend_scope_name(scoped_name, "",
						null_template_arg_array);
		return;
	}
	calc_name_data.count = 0;
	
	for (src = format; *src; ) {
		if (*src != '%') {
			for (len = 0; (src[len] && (src[len] != '%')); ++len)
				/* Do nothing. */ ;
			CALC_NAME_ADD_COMPONENT(src, len);
			src += len;
			
			if (!(*src))
				break;
		}
		switch (*(++src)) {
		default:
			panic("In `pg_state::calc_scoped_name', "
			      "unknown dispatch macro `%%%c'.", *src);
			break;
		case 'n':
			CALC_NAME_ADD_COMPONENT(a(ref).name,
						strlen(a(ref).name));
			break;
		}
		++src;
	}
	
	/*
	 * Concatenate all of the components into the final name string.
	 */
	result_len = 1; /* 1 for terminating NUL. */
	for (i = 0; i < calc_name_data.count; ++i)
		result_len += calc_name_data.components[i].len;
	
	result_str = (char *) mustmalloc(sizeof(char) * result_len);
	
	for (i = 0, dst = result_str; i < calc_name_data.count; ++i) {
		strncpy(dst,
			calc_name_data.components[i].str,
			calc_name_data.components[i].len);
		dst += calc_name_data.components[i].len;
	}
	*dst = 0;

	// fprintf(stderr, "  In:    `%s' `%s'\n", format, object_name);
	// fprintf(stderr, "    Out: `%s'\n", result_str);
	calc_scoped_name(scoped_name,
			 aoi_get_parent_scope(in_aoi, ref),
			 format);
	cast_add_scope_name(scoped_name, result_str, null_template_arg_array);
}

/*****************************************************************************/

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1