/*
 *   VHDL Developer's Toolkit using C++
 *	Semantic Definition Routine
 */

#include "basicMan.h"
#include "designMan.h"
#include "libraryMan.h"
#include "glossary.h"
#include "semantic.h"
#include "lexem.h"


void vs_error(int ln, char* pos, char* msg)
{
	errCount++;
	fprintf(stderr, "%s(%d): error(%s): ", vhdlFile, ln, pos);
	fprintf(stderr, "%s\n", msg);
}

int isAssociationAllowed(oid fpart, oid apart)
{
	// it can be Open association.
	if (!apart) return 1;

	// extract formal designator.
	oid formal = fpart;
	if (fpart->isRight(OP_type_conv))
		formal = fpart->get_expression();
	else if (fpart->isRight(OP_func_call))
		formal = fpart->gen_parameter_maps().front()->get_actual();

	// extract actual designator.
	oid actual = apart;
	if (fpart->isRight(OP_type_conv))
		actual = apart->get_expression();
	else if (apart->isRight(OP_func_call))
		actual = apart->gen_parameter_maps().front()->get_actual();

	switch (actual->get_object_type()) {
	case O_operator:
		switch (actual->get_operator_kind()) {
		case OP_selected:
		case OP_indexed:
		case OP_slice:
			actual = actual->get_prefix();
			return isAssociationAllowed(fpart, actual);
		}
		break;
	case O_alias:
		actual = actual->get_aliased_object();
		return isAssociationAllowed(fpart, actual);
	}

	// check the object class.
	switch (formal->get_object_type()) {
	case O_constant:
		if (!actual->isExpression())
			return 0;
		break;
	case O_signal:
		if (!actual->isRight(O_signal))
			return 0;
		break;
	case O_variable:
		if (!actual->isRight(O_variable) && !actual->isRight(O_file))
			return 0;
		if (formal->get_type()->isRight(T_file))
			if (apart->isRight(OP_func_call))
				return 0;
		break;
	}

	// check the mode of association.
	switch (formal->get_mode_kind()) {
	case M_in:
		if (!actual->isObject())
			break;
		if (actual->get_mode_kind() == M_null)
			break;
		if (actual->get_mode_kind() == M_in)
			break;
		if (actual->get_mode_kind() == M_inout)
			break;
		if (actual->get_mode_kind() == M_buffer)
			break;
		return 0;
	case M_out:
		if (!actual->isRight(O_variable) && !actual->isRight(O_file))
			if (!actual->isRight(O_signal))
				return 0;
		if (actual->get_mode_kind() == M_null)
			break;
		if (actual->get_mode_kind() == M_out)
			break;
		if (actual->get_mode_kind() == M_inout)
			break;
		return 0;
	case M_inout:
		if (!actual->isRight(O_variable) && !actual->isRight(O_file))
			if (!actual->isRight(O_signal))
				return 0;
		if (actual->get_mode_kind() == M_null)
			break;
		if (actual->get_mode_kind() == M_inout)
			break;
		return 0;
	case M_buffer:
		if (!actual->isRight(O_variable) && !actual->isRight(O_file))
			if (!actual->isRight(O_signal))
				return 0;
		if (actual->get_mode_kind() == M_null)
			break;
		if (actual->get_mode_kind() == M_buffer)
			break;
		return 0;
	case M_linkage:
		if (!actual->isRight(O_variable) && !actual->isRight(O_file))
			if (!actual->isRight(O_signal))
				return 0;
		break;
	}

	// check the index range of formal part.
	switch (formal->get_mode_kind()) {
	case M_in:
		if (apart->isRight(OP_func_call) || apart->isRight(OP_type_conv))
			if (isUnconstrainedType(getEvalType(apart)))
				return 0;
		break;
	case M_out:
	case M_buffer:
		if (fpart->isRight(OP_func_call) || fpart->isRight(OP_type_conv))
			if (isUnconstrainedType(getEvalType(fpart)))
				return 0;
		break;
	case M_inout:
	case M_linkage:
		if (apart->isRight(OP_func_call) || apart->isRight(OP_type_conv))
			if (isUnconstrainedType(getEvalType(apart)))
				return 0;
		if (fpart->isRight(OP_func_call) || fpart->isRight(OP_type_conv))
			if (isUnconstrainedType(getEvalType(fpart)))
				return 0;
		break;
	}
	return 1;
}

int isAssociationAllowed(oid obj)
{
	if (!obj) return 1;

	switch (obj->get_object_type()) {
	case O_comp_conf:
	case O_conf_spec: {
		// the entity aspect may be Open.
		if (!obj->get_entity_aspect())
			return 1;
		oid unit = obj->get_entity_aspect();
		if (unit->get_object_type() != O_entity)
			unit = unit->get_entity();
		gid generics = unit->gen_generics();
		gid gob = obj->gen_generic_maps();
		if (!gob.empty())
			if (!isAssociationAllowed(gob, generics))
				return 0;
		gid ports = unit->gen_ports();
		gid goc = obj->gen_port_maps();
		if (!goc.empty())
			if (!isAssociationAllowed(goc, ports))
				return 0;
		return 1; }
	case O_statement:
		switch (obj->get_stmt_kind()) {
		case ST_block: {
			gid generics = obj->gen_generics();
			if (!isAssociationAllowed(obj->gen_generic_maps(), generics))
				return 0;
			gid ports = obj->gen_ports();
			if (!isAssociationAllowed(obj->gen_port_maps(), ports))
				return 0;
			return 1; }
		case ST_comp_inst: {
			if (!obj->get_component())
				return 1;
			gid generics = obj->get_component()->gen_generics();
			if (!isAssociationAllowed(obj->gen_generic_maps(), generics))
				return 0;
			gid ports = obj->get_component()->gen_ports();
			if (!isAssociationAllowed(obj->gen_port_maps(), ports))
				return 0;
			return 1; }
		case ST_conc_proc_call:
		case ST_proc_call: {
			if (!obj->get_procedure())
				return 1;
			gid gob = obj->get_procedure()->gen_parameters();
			if (!isAssociationAllowed(obj->gen_parameter_maps(), gob))
				return 0;
			return 1; }
		}
		break;
	case O_operator: {
		if (obj->get_operator_kind() != OP_func_call)
			break;
		if (!obj->get_function())
			return 1;
		gid gob = obj->get_function()->gen_parameters();
		if (!isAssociationAllowed(obj->gen_parameter_maps(), gob))
			return 0;
		return 1; }
	}
	pi_error(Error, "isAssociationAllowed()", "illegal appearance.");
	return 1;
}

int isAssociationAllowed(gid gob, gid formals)
{
	if (formals.empty())
		return 1;
	if (gob.length() > formals.length())
		return 0;
	for (Pix cur = gob.first(); cur; gob.next(cur)) {
		// extract the formal designator from association element.
		oid formal = gob(cur)->get_formal();
		if (formal->isRight(OP_func_call))
			formal = formal->gen_parameter_maps().front();
		if (formal->isRight(OP_arrow))
			formal = formal->get_actual();
		// subtract the formal designator from interface list.
		gid gob;
		while (!formals.empty()) {
			oid obj = formals.remove_front();
			if (obj != formal)
				gob.append(obj);
		}
		formals.join(gob);
	}
	// the formals have not default expression.
	for (Pix cur = formals.first(); cur; formals.next(cur))
		if (!formals(cur)->get_initial_expr())
			return 0;
	return 1;
}

int isAssociationFormal(oid fpart)
{
	if (!fpart || !fpart->isExpression()) {
		pi_error(Error, "isAssociationFormal()", "illegal appearance.");
		return 1;
	}

	// extract formal designator.
	oid formal = fpart;
	if (fpart->isRight(OP_func_call)) {
		gid gob = fpart->gen_parameter_maps();
		if (gob.length() != 1)
			return 0;
		formal = gob.front()->get_actual();
	} else if (fpart->isRight(OP_type_conv))
		formal = fpart->get_expression();

	// it must be an interface object.
	if (!formal->isObject())
		return 0;
	if (formal->get_mode_kind() == M_null)
		return 0;
	if (formal->isRight(O_signal))
		if (!fpart->isRight(O_signal))
			return 0;
	return 1;
}

int isAssociationActual(oid apart)
{
	// it can be Open association.
	if (!apart) return 1;
	if (!apart->isExpression()) {
		pi_error(Error, "isAssociationActual()", "illegal appearance.");
		return 1;
	}

	// extract actual designator.
	oid actual = apart;
	if (apart->isRight(OP_func_call)) {
		gid gob = apart->gen_parameter_maps();
		if (gob.length() != 1)
			return 0;
		actual = gob.front()->get_actual();
	} else if (apart->isRight(OP_type_conv))
		actual = apart->get_expression();

	// it must be an expression.
	if (isSignalName(actual))
		if (!isSignalName(apart) || !isStaticName(apart, _Global))
			return 0;
/* ???
	} else if (!isVariableName(actual))	
		if (!isStaticExpr(apart, _Global))
			return 0;
*/
	return 1;
}

int isChoiceAllowed(oid obj, gid choices)
{
	for (Pix cur = choices.first(); cur; choices.next(cur))
		if (!isChoiceAllowed(obj, choices(cur)))
			return 0;
	return 1;
}

int isChoiceAllowed(oid obj, oid choice)
{
	if (!choice) return 1;
	if (!obj || !choice->isChoice()) {
		pi_error(Error, "isChoiceAllowed()", "illegal appearance.");
		return 1;
	}
	
	switch (obj->get_object_type()) {
	case O_statement:
		switch (obj->get_stmt_kind()) {
		case ST_case_alt:
		case ST_sel_wave: {
			oid expr = obj->get_scope()->get_expression();
			// the choice must be of the same type as the case expression.
			if (choice->isRangeConstraint()) {
				if (!isRangeAllowed(getEvalType(expr), choice, Loose))
					return 0;
			} else if (choice->isRight(OP_range)) {
				if (!isBaseTypeSame(expr, choice->get_type()))
					return 0;
			} else if (!isBaseTypeSame(expr, choice))
				return 0;
			if (!isStringType(getEvalType(expr)))
				return 1;
			// the choice must be of the same length as the case expression.
			return 1; }
		}
		break;
	case O_operator:
		if (obj->get_operator_kind() == OP_choice) {
			if (!choice->isRight(T_field))
				return 1;
			return isBaseTypeSame(obj->get_expression(), choice);
		}
		break;
	}
	return 0;
}

int isTypeAllowed(oid obj, oid type)
{
	if (!type) return 1;
	if (!obj || !type->isRight(O_type)) {
		pi_error(Error, "isTypeAllowed()", "illegal appearance.");
		return 1;
	}

	switch (obj->get_object_type()) {
	case O_constant:
		//??? it can be an interface constant.
		if (!obj->get_scope())
			return 1;
		if (obj->get_scope()->isRight(O_subprogram))
			return 1;
		if (type->get_type_kind() == T_incomplete)
			return 0;
		if (type->get_type_kind() == T_access)
			return 0;
		return !type->isRight(T_file);
	case O_signal:
		if (type->get_type_kind() == T_incomplete)
			return 0;
		if (type->get_type_kind() == T_access)
			return 0;
		if (type->get_type_kind() == T_file)
			return 0;
		// it can be of an unconstrained type.
		if (obj->get_mode_kind() == M_null)
			if (obj->gen_ranges().empty())
				return !isUnconstrainedType(type);
		return 1;
	case O_variable:
		//??? it can be a interface variable.
		if (obj->get_mode_kind() != M_null)
			return 1;
		if (type->get_type_kind() == T_incomplete)
			return 0;
		if (type->get_type_kind() == T_file)
			return 0;
		// it can be of an unconstrained type.
		if (obj->get_mode_kind() == M_null)
			if (obj->gen_ranges().empty())
				return !isUnconstrainedType(type);
		return 1;
	case O_file:
		if (type->get_type_kind() == T_file)
			return 1;
		return 0;
	case O_alias:
		if (type->get_type_kind() == T_incomplete)
			return 0;
		if (type->get_type_kind() == T_array)
			if (type->gen_indexs().length() > 1)
				return 0;
		return 1;
	case O_attribute:
		if (type->get_type_kind() == T_incomplete)
			return 0;
		if (type->get_type_kind() == T_access)
			return 0;
		return !type->isRight(T_file);
		break;
	case O_type:
		// the incomplete type can be used as a base type of access type.
		if (type->get_type_kind() == T_incomplete)
			return obj->isRight(T_access);

		switch (obj->get_type_kind()) {
		case T_array:
			if (type->get_type_kind() == T_file)
				return 0;
			if (obj->gen_ranges().empty())
				return !isUnconstrainedType(type);
			return 1;
		case T_field:
			if (type->get_type_kind() == T_field)
				return 0;	
			if (type->get_type_kind() == T_file)
				return 0;
			if (obj->gen_ranges().empty())
				return !isUnconstrainedType(type);
			return 1;
		case T_subtype:
			if (!obj->get_res_func())
				return 1;
			if (type->get_type_kind() == T_file)
				return 0;
			if (type->get_type_kind() == T_access)
				return 0;
			return 1;
		case T_access:
			if (type->get_type_kind() == T_file)
				return 0;
			return 1;
		case T_file:
			if (type->get_type_kind() == T_file)
				return 0;
			if (type->get_type_kind() == T_access)
				return 0;
			if (type->get_type_kind() == T_record) {
				// it must not contain a subelement of an access type.
				gid gob = type->gen_fields();
				for (Pix cur = gob.first(); cur; gob.next(cur))
					if (gob(cur)->get_type()->isRight(T_access)) 
						return 0;
				return 1;
			}
			if (type->get_type_kind() == T_array) {
				// it must be one-dimensional array type.
				if (type->gen_indexs().length() != 1)
					return 0;
				// it must not contain a subelement of an access type.
				return type->get_type()->isRight(T_access) ? 0:1;
			}
			return 1;
		}
		break;
	case O_statement:
		switch (obj->get_stmt_kind()) {
		case ST_case:
		case ST_sel_sig_assign:
			if (isDiscreteType(type))
				return 1;
 			if (!isStringType(type))
				return 0;
			// it may be an object whose subtype is locally static.
			if (obj->get_expression()->isObject()) {
				gid gob = obj->get_expression()->gen_ranges();
				if (gob.empty())
					return isStaticType(type, _Local);
				for (Pix cur = gob.first(); cur; gob.next(cur))
					if (!isStaticDiscreteRange(gob(cur), _Local))
						return 0;
				return 1;
			}
			return isStaticType(type, _Local);
		}
		break;
	}
	return 0;
}

int isBaseTypeSame(char* nm, oid type)
{
	if (!type)
		return 1;
	oid obj = getStandardType(nm);
	return isBaseTypeSame(obj, type);
}

int isBaseTypeSame(oid obj, gid gob)
{
	for (Pix cur = gob.first(); cur; gob.next(cur))
		if (!isBaseTypeSame(obj, gob(cur)))
			return 0;
	return 1;
}

int isBaseTypeSame(oid obj, oid type)
{
	if (!obj || !type) return 1;
	if (!obj->isRight(O_type) && !obj->isExpression()) {
		pi_error(Error, "isBaseTypeSame()", "illegal appearance.");
		return 1;
	}
	if (!type->isRight(O_type) && !type->isExpression()) {
		pi_error(Error, "isBaseTypeSame()", "illegal appearance.");
		return 1;
	}

	// it can be a literal.
	if (obj->isRight(O_literal) && type->isRight(O_literal))
		return (obj->get_literal_kind() == type->get_literal_kind()) ? 1:0;
	if (obj->isRight(O_literal)) {
		type = getBaseType(getEvalType(type));
		switch (obj->get_literal_kind()) {
		case L_integer:
			return (type->get_type_kind() == T_integer) ? 1:0;
		case L_float:
			return (type->get_type_kind() == T_float) ? 1:0;
		case L_string:
			return (isStringType(type)) ? 1:0;
		case L_bit_string:
			// it must be one-dimensional array of Bit type.
			if (!type->isRight(T_array))
				return 0;
	 		if (type->gen_indexs().length() != 1)
				return 0;
			type = type->get_type();
			return isBaseTypeSame(getStandardType("bit"), type);
		}
		return 0;
	}
	if (type->isRight(O_literal)) {
		obj = getBaseType(getEvalType(obj));
		switch (type->get_literal_kind()) {
		case L_integer:
			return (obj->get_type_kind() == T_integer) ? 1:0;
		case L_float:
			return (obj->get_type_kind() == T_float) ? 1:0;
		case L_string:
			return (isStringType(obj)) ? 1:0;
		case L_bit_string:
			// it must be one-dimensional array of Bit type.
			if (!obj->isRight(T_array))
				return 0;
	 		if (obj->gen_indexs().length() != 1)
				return 0;
			obj = obj->get_type();
			return isBaseTypeSame(getStandardType("bit"), obj);
		}
		return 0;
	}

	// it can be an access type.
	obj = getBaseType(getEvalType(obj));
	type = getBaseType(getEvalType(type));
	if (obj->get_type_kind() == T_access)
		obj = getBaseType(obj->get_type());
	if (type->get_type_kind() == T_access)
		type = getBaseType(type->get_type());
	return (obj == type) ? 1:0;
}

int isFlowAllowed(oid obj, oid scope)
{
	if (!scope) return 1;
	if (!obj) {
		pi_error(Error, "isFlowAllowed()", "illegal appearance.");
		return 1;
	}

	switch (obj->get_object_type()) {
	case O_constant:	
		// it may be a loop(generator) parameter.
		if (scope->isRight(ST_generate) || scope->isRight(ST_loop))
			return isDiscreteRange(obj->gen_ranges());
		// the default expression of interface constant must be static.
		if (obj->get_mode_kind() != M_null)
			return isStaticExpr(obj->get_initial_expr(), _Global);
		// it may be a deferred constant.
		if (!obj->get_initial_expr())
			return (scope->isRight(O_package)) ? 1:0;
		return 1;
	case O_signal:
		if (obj->get_mode_kind() == M_null)
			return 1;
		// it is an interface signal.
		if (scope->isRight(O_subprogram))
			if (obj->get_signal_kind() != S_null)
				return 0;
		if (scope->isRight(O_subprogram))
			if (obj->get_initial_expr())
				return 0;
		if (obj->get_mode_kind() == M_linkage)
			if (obj->get_initial_expr())
				return 0;
		return isStaticExpr(obj->get_initial_expr(), _Global);
	case O_variable:
		if (obj->get_mode_kind() == M_null)
			return 1;
		// it may be a interface variable of file type in function.
		if (scope->get_line_number() > 0)
			if (!scope->isProcedure())
				return 0;
		// it is an interface variable.
		if (obj->get_mode_kind() != M_in)
			if (obj->get_initial_expr())
				return 0;
		if (obj->get_type()->isRight(T_file))
			if (obj->get_initial_expr())
				return 0;
		return isStaticExpr(obj->get_initial_expr(), _Global);
	case O_statement:
		switch (obj->get_stmt_kind()) {
		case ST_wait:
			while (scope->isSequStmt())
				scope = scope->get_scope();
			if (scope->isRight(SP_func_body))
				return 0;
			if (scope->isRight(ST_process))
				if (!scope->gen_sensitivities().empty())
					return 0;
			return 1;
		case ST_case_alt:
			// it may have Others choice.
			if (obj->gen_choices().empty())
				if (scope->gen_case_alts().rear() != obj)
					return 0;
			return 1;
		case ST_sel_wave:
			// it may have Others choice.
			if (obj->gen_choices().empty())
				if (scope->gen_sel_waves().rear() != obj)
					return 0;
			return 1;
		case ST_next:
		case ST_exit:
			while (scope->isSequStmt() && !scope->isRight(ST_loop))
				scope = scope->get_scope();
			return scope->isRight(ST_loop);
		case ST_return:
			while (scope->isSequStmt())
				scope = scope->get_scope();
			if (scope->isRight(SP_func_body))
				if (obj->get_expression())
					return 1;
			if (scope->isRight(SP_proc_body))
				if (!obj->get_expression())
					return 1;
			return 0;
		case ST_proc_call: {
			if (!obj->get_procedure()->isRight(SP_proc_body))
				return 1;
			int flag = 0;
			gid gob = obj->get_procedure()->gen_sequ_stmts();
			for (Pix cur = gob.first(); cur; gob.next(cur))
				if (gob(cur)->isRight(ST_wait))
					flag++;
			if (!flag)
				return 1;
			// check that the scope is process statement.
			if (!scope->isRight(ST_process))
				return isFlowAllowed(obj, scope->get_scope());
			// check that the scope has sensitivities.
			if (!scope->gen_sensitivities().empty())
				return 0;
			gob = scope->gen_sequ_stmts();
			for (Pix cur = gob.first(); cur; gob.next(cur))
				if (gob(cur)->isRight(ST_wait))
					return 0;
			return 1; }
		case ST_process:
			if (scope->get_object_type() == O_entity)
				return isPassiveStatement(obj);
			return 1;
		case ST_conc_proc_call:
			if (obj->get_procedure()) {
				gid gob = obj->get_procedure()->gen_parameters();
				for (Pix cur = gob.first(); cur; gob.next(cur))
					if (gob(cur)->isRight(O_variable))
						return 0;
			}
			if (scope->get_object_type() == O_entity)
				return isPassiveStatement(obj);
			return 1;
		case ST_waveform:
			if (scope->isRight(ST_sig_assign))
				if (isGuardedTarget(scope->get_target()))
					return 1;
			// it can be a Null waveform.
			if (!obj->get_value_expr())
				return 0;
			return 1;
		}
		break;
	case O_type:
		if (obj->get_type_kind() == T_incomplete) {
			gid gob = scope->search(obj->get_name());
			for (Pix cur = gob.first(); cur; gob.next(cur))
				if (gob(cur)->get_type_kind() != T_incomplete)
					return 1;
			return 0;
		}
		break;
	}
	return 0;
}

int isModeAllowed(oid obj, oid scope)
{
	if (!scope) return 1;
	if (!obj || !obj->hasName()) {
		pi_error(Error, "isModeAllowed()", "illegal appearenc.");
		return 1;
	}

	switch (obj->get_object_type()) {
	case O_constant:
		if (obj->get_mode_kind() == M_null)
			return 1;
		if (obj->get_mode_kind() == M_in)
			return 1;
		return 0;
	case O_signal:
		if (scope->isFunction())
			if (obj->get_mode_kind() != M_in)
				return 0;
		return 1;
	case O_file:
		if (obj->get_mode_kind() == M_in)
			return 1;
		if (obj->get_mode_kind() == M_out)
			return 1;
		return 0;
	}
	return 0;
}

int isModeReadable(gid gob)
{
	for (Pix cur = gob.first(); cur; gob.next(cur))
		if (!isModeReadable(gob(cur)))
			return 0;
	return 1;
}

int isModeReadable(oid obj)
{
	// the actual part may be Open.
	if (!obj) return 1;
	if (!obj->isExpression()) {
		pi_error(Error, "isModeReadable()", "illegal appearance.");
		return 1;
	}

	switch (obj->get_object_type()) {
	case O_constant:
	case O_signal:
	case O_variable:
		switch (obj->get_mode_kind()) {
		case M_null: case M_in: case M_buffer:
		case M_inout: case M_linkage:
			return 1;
		}
		return 0;
	case O_alias:
		return isModeReadable(obj->get_aliased_object());
	case O_operator:
		switch (obj->get_operator_kind()) {
		case OP_selected:
		case OP_indexed:
		case OP_slice:
			return isModeReadable(obj->get_prefix());
		}
		if (obj->isRight(OP_aggregate)) {
			gid gob = obj->gen_elements();
			for (Pix cur = gob.first(); cur; gob.next(cur)) {
				oid element = gob(cur);
				if (element->isRight(OP_choice))
					element = element->get_expression();
				if (!isModeReadable(element))
					return 0;
			}
			return 1;
		}
		if (obj->isRight(OP_attribute)) {
			oid prefix = obj->get_prefix();
			if (!prefix->isExpression())
				return 1;
			if (!prefix->isObject())
				return isModeReadable(prefix);
			switch (prefix->get_mode_kind()) {
			case M_in:
				// it may be an interface signal of subprogram.
				if (prefix->get_scope()->isRight(O_subprogram))
					if (prefix->isRight(O_signal))
						return !isSignalKind(obj);
				break;
			case M_out:
				switch (obj->get_attr_kind()) {
				case A_event: case A_active: case A_last_event:
				case A_last_active: case A_last_value:
					return !prefix->isRight(O_signal);
				}
				if (prefix->isRight(O_signal))
					return !isSignalKind(obj);
				break;
			case M_inout:
				return !isSignalKind(obj);
			case M_linkage:
				return 0;
			}
			return 1;
		}
		break;
	}
	return 1;
}

int isModeWriteable(gid gob)
{
	for (Pix cur = gob.first(); cur; gob.next(cur))
		if (!isModeWriteable(gob(cur)))
			return 0;
	return 1;
}

int isModeWriteable(oid obj)
{
	// the actual part may be Open.
	if (!obj || !obj->isExpression()) {
		pi_error(Error, "isModeWriteable()", "illegal appearance.");
		return 1;
	}

	switch (obj->get_object_type()) {
	case O_signal:
	case O_variable:
		switch (obj->get_mode_kind()) {
		case M_null: case M_out: case M_buffer:
		case M_inout: case M_linkage:
			return 1;
		}
		return 0;
	case O_alias:
		return isModeWriteable(obj->get_aliased_object());
	case O_operator:
		if (obj->isRight(OP_aggregate)) {
			gid gob = obj->gen_elements();
			for (Pix cur = gob.first(); cur; gob.next(cur)) {
				oid element = gob(cur);
				if (element->isRight(OP_choice))
					element = element->get_expression();
				if (!isModeWriteable(element))
					return 0;
			}
			return 1;
		}
		switch (obj->get_operator_kind()) {
		case OP_selected:
		case OP_indexed:
		case OP_slice:
			return isModeWriteable(obj->get_prefix());
		}
		return 0;
	}
	return 0;
}

int isNameAllowed(oid obj, oid name)
{
	if (!name) return 1;
	if (!obj) {
		pi_error(Error, "isNameAllowed()", "illegal appearance.");
	}

	switch (obj->get_object_type()) {
	case O_operator:
		switch (obj->get_operator_kind()) {
		case OP_selected:
			if (name->get_object_type() != O_operator)
				return 1;
			if (name->get_operator_kind() != OP_func_call)
				return 1;
			return 0;
		case OP_slice:
			name = getBaseType(getEvalType(name));
			if (name->get_type_kind() != T_array)
				return 0;
			if (name->gen_indexs().length() != 1)
				return 0;
			return 1;	
		}
		return 0;
	case O_alias:
		if (name->isObject() || name->isRight(O_alias))
			return 1;
		if (name->isNameOperator())
			return isNameAllowed(obj, name->get_prefix());
		return 0;
	case O_subprogram:
		if (obj->isProcedure())
			return (obj->get_name()[0] == '"') ? 0:1;
		// the function designator may be an operator symbol.
		if (obj->get_name()[0] == '"') {
			String str = downcase(obj->get_name());
			str = str(1, strlen(str)-2);
			for (int i = 1; strcmp(operatorKind[i], "$"); i++)
				if (str == operatorKind[i])
					return 1;
			return 0;
		}
		return 1;
	}
	return 0;
}

int isNameUnique(oid obj, char* nm)
{
	if (!nm) return 1;
	if (!obj || !obj->get_scope()) {
		pi_error(Error, "isNameUnique()", "illegal appearance.");
		return 1;
	}

	gid goc, gob;
	switch (obj->get_object_type()) {
	case O_constant:
		gob = obj->get_scope()->search(nm);
		for (Pix cur = gob.first(); cur; gob.next(cur)) {
			// it can be a deferred constant.
			if (gob(cur)->isRight(O_constant))
				if (!gob(cur)->get_initial_expr())
					continue;
			goc.append(gob(cur));
		}
		return (goc.length() > 1) ? 0:1;
	case O_type:
		// the scope may be enumeration type, or physical type.
		switch (obj->get_type_kind()) {
		case T_element:
		case T_unit:
			gob = obj->get_scope()->get_scope()->search(nm);
			for (Pix cur = gob.first(); cur; gob.next(cur))
				if (gob(cur)->get_scope() == obj->get_scope())
					goc.append(gob(cur));
			break;
		default:
			gob = obj->get_scope()->search(nm);
			for (Pix cur = gob.first(); cur; gob.next(cur))
				// it can be incomplete type.
				if (!gob(cur)->isRight(T_incomplete))
					goc.append(gob(cur));
			break;
		}
		return (goc.length() > 1) ? 0:1;
	}

	oid scope = obj->get_scope();
	while (!scope->hasSearch())
		scope = scope->get_scope();
	goc = scope->search(nm);
	return (goc.length() > 1) ? 0:1;
}

int isRangeAllowed(oid obj, gid ranges, int flag)
{
	if (ranges.empty()) return 1;
	if (!obj || !obj->isRight(O_type)) {
		pi_error(Error, "isRangeAllowed()", "illegal appearance.");
		return 1;
	}

	// it can be an index constraint.
	oid type = obj;
	while (type->isRight(T_access) || type->isRight(T_subtype))
		type = type->get_type();
	if (type->get_type_kind() == T_array) {
		// it should be an unconstrained array type.
		if (flag && !isUnconstrainedType(obj))
			return 0;
		gid indexes = obj->gen_indexs();
		if (indexes.length() != ranges.length())
			return 0;
		for (Pix cur = ranges.first(); cur; ranges.next(cur)) {
			oid index = indexes.remove_front();
			// implicit conversion to the Integer type.
			oid type = getStandardType("integer");
			if (!index->isRangeConstraint())
				type = getEvalType(index);
			if (ranges(cur)->isRangeConstraint()) {
				if (!isRangeAllowed(type, ranges(cur), Tight))
					return 0;
			} else if (!isBaseTypeSame(type, getEvalType(ranges(cur))))
				return 0;
		}
		return 1;
	}

	// it can be a range constraint.
	for (Pix cur = ranges.first(); cur; ranges.next(cur)) {
		if (!ranges(cur)->isRangeConstraint())
			return 0;
		if (!isRangeAllowed(obj, ranges(cur), Tight))
			return 0;
	}
	return 1;
}

int isRangeAllowed(oid type, oid range, int flag)
{
	// it can be an unconstrained range.
	if (!range) return 1;

	// the range must be range constraint.
	if (!type || !type->isRight(O_type) || !range->isRangeConstraint()) {
		pi_error(Error, "isRangeAllowed()", "illegal appearance.");
		return 1;
	}

	// it may be a scalar type.
	oid obj = type;
	while (obj->isRight(T_access) || obj->isRight(T_subtype))
		obj = obj->get_type();
	if (!isScalarType(obj))
		return 0;

	if (range->isRight(OP_attribute))
		return isBaseTypeSame(type, getEvalType(range));
	oid left = range->get_left();
	oid right = range->get_right();

	switch (obj->get_type_kind()) {
	case T_integer:
	case T_physical:
		if (flag) break;
		if (left->isRight(O_literal)) {
			if (left->get_literal_kind() != L_integer)
				return 0;
		} else if (!getBaseType(getEvalType(left))->isRight(T_integer))
			return 0;
		if (!isRangeBelong(type, left))
			return 0;
		if (right->isRight(O_literal)) {
			if (right->get_literal_kind() != L_integer)
				return 0;
		} else if (!getBaseType(getEvalType(right))->isRight(T_integer))
			return 0;
		if (!isRangeBelong(type, right))
			return 0;
		return 1;
	case T_float:
		if (left->isRight(O_literal)) {
			if (left->get_literal_kind() != L_float)
				return 0;
		} else if (!getBaseType(getEvalType(left))->isRight(T_float))
			return 0;
		if (!isRangeBelong(type, left))
			return 0;
		if (right->isRight(O_literal)) {
			if (right->get_literal_kind() != L_float)
				return 0;
		} else if (!getBaseType(getEvalType(right))->isRight(T_float))
			return 0;
		if (!isRangeBelong(type, right))
			return 0;
		return 1;
	}

	if (!isBaseTypeSame(type, left))
		return 0;
  	if (!isBaseTypeSame(type, right))
		return 0;
 	if (!isRangeBelong(type, left))
		return 0;
 	if (!isRangeBelong(type, right))
		return 0;
	return 1;
}

int isRangeBelong(oid type, oid value)
{
	return 1;
	if (!value) return 1;
	if (!type || !type->isRight(O_type)) {
		pi_error(Error, "isRangeBelong()", "illegal appearance.");
		return 1;
	}

	// extract float value from range expression.
	float fl = -1.0E38;
	float fr = +1.0E38;
	switch (type->get_type_kind()) {
	case T_enum:
		fl = 0.0;
		fr = type->gen_elements().length() - 1;
		break;
	case T_subtype: {
		if (type->gen_ranges().empty())
			return isRangeBelong(type->get_type(), value);
		oid obj, range = type->gen_ranges().front();
		if (obj = getRangeBound(range, _Left))
			fl = obj->get_flag();	
		if (obj = getRangeBound(range, _Right))
			fr = obj->get_flag();
		} break;
	case T_physical:
	case T_integer:
	case T_float: {
		oid obj, range = type->get_range();
		if (obj = getRangeBound(range, _Left))
			fl = obj->get_flag();	
		if (obj = getRangeBound(range, _Right))
			fr = obj->get_flag();
		} break;
	}

	// extract float value from value expression.
	if (!getExprValue(value))
		return 1;
	float fval = value->get_flag();
	if (fval < fl || fval > fr)
		return 0;
	return 1;
}

int isTargetAllowed(oid obj, oid target)
{
	if (!target) return 1;
	if (!obj || obj->get_object_type() != O_statement) {
		pi_error(Error, "isTargetAllowed()", "illegal appearance.");
		return 1;
	}

	switch (obj->get_stmt_kind()) {
	case ST_cond_sig_assign:
	case ST_sel_sig_assign:
	case ST_sig_assign:
		if (target->isRight(OP_aggregate)) {
			gid gob = target->gen_elements();
			for (Pix cur = gob.first(); cur; gob.next(cur)) {
				oid element = gob(cur);
				if (element->isRight(OP_choice))
					element = element->get_expression();
				if (!isStaticName(element, _Local))
					return 0;
				if (!isTargetAllowed(obj, element))
					return 0;
			}
			return 1;
		}
		// it may not be an aggregate.
		if (!isSignalName(target))
			return 0;
		if (!isModeWriteable(target))
			return 0;
		return 1;
	case ST_var_assign:
		if (target->isRight(OP_aggregate)) {
			gid gob = target->gen_elements();
			for (Pix cur = gob.first(); cur; gob.next(cur)) {
				oid element = gob(cur);
				if (element->isRight(OP_choice))
					element = element->get_expression();
				if (!isStaticName(element, _Local))
					return 0;
				if (!isTargetAllowed(obj, element))
					return 0;
			}
			return 1;
		}
		// it may not be an aggregate.
		if (!isVariableName(target))
			return 0;
		if (!isModeWriteable(target))
			return 0;
		return 1;
	}
	return 0;
}

int isTargetGuarded(oid obj, oid target)
{
	if (!target) return 1;
	if (!obj || obj->get_object_type() != O_statement) {
		pi_error(Error, "isTargetGuarded()", "illegal appearance.");
		return 1;
	}

	switch (obj->get_stmt_kind()) {
	case ST_cond_sig_assign:
	case ST_sel_sig_assign:
		if (target->isRight(OP_aggregate)) {
			gid gob = target->gen_elements();
			int flag = 0;
			for (Pix cur = gob.first(); cur; gob.next(cur)) {
				oid element = gob(cur);
				if (element->isRight(OP_choice))
					element = element->get_expression();
				if (isGuardedTarget(element))
					flag++;
			}
			if (flag > 0 && flag < gob.length())
				return 0;
		}
		if (!isGuardedTarget(target))
			return 1;
		// it must be a guarded target.
		if (obj->get_assign_option() == AO_guarded_inertial)
			return 1;
		if (obj->get_assign_option() == AO_guarded_transport)
			return 1;
		break;
	}
	return 0;
}

int isAttributePrefix(oid obj, oid prefix)
{
	if (!prefix) return 0;
	if (!obj || !obj->isRight(OP_attribute)) {
		pi_error(Error, "isAttributePrefix()", "illegal appearance.");
		return 1;
	}

	// it can be user defined attribute.
	if (obj->get_attr_kind() == A_user) {
		while (prefix->get_object_type() == O_alias)
			prefix = prefix->get_aliased_object();
		gid gob = obj->get_attribute()->gen_attr_specs();
		for (Pix cur = gob.first(); cur; gob.next(cur)) {
			gid goc = gob(cur)->gen_attr_objects();
			for (Pix pos = goc.first(); pos; goc.next(pos))
				if (goc(pos) == prefix)
					return 1;
		}
		return 0;
	}

	switch (obj->get_attr_kind()) {
	case A_base:
		// the prefix must be type.
		if (prefix->isRight(OP_attribute) && isTypeKind(prefix))
			prefix = getResultType(prefix);
		return (prefix->get_object_type() == O_type) ? 1:0;
	case A_left:
	case A_right:
	case A_high:
	case A_low:
	case A_ascending:
	case A_image:
	case A_value:
		// the prefix must be scalar type.
		if (prefix->isRight(OP_attribute) && isTypeKind(prefix))
			prefix = getResultType(prefix);
		if (prefix->get_object_type() != O_type)
			return 0;
		return isScalarType(getBaseType(prefix));
	case A_pos:
	case A_val:
	case A_succ:
	case A_pred:
	case A_leftof:
	case A_rightof:
		// the prefix must be discrete type, or physical type.
		if (prefix->isRight(OP_attribute) && isTypeKind(prefix))
			prefix = getBaseType(getResultType(prefix));
		if (prefix->get_object_type() != O_type)
			return 0;
		if (isDiscreteType(prefix) || prefix->isRight(T_physical))
			return 1;
		return 0;
	case A_left_:
	case A_right_:
	case A_high_:
	case A_low_:
	case A_range:
	case A_reverse_range:
	case A_length:
	case A_ascending_:
		// the prefix must be array type, or array object.
		return getBaseType(getEvalType(prefix))->isRight(T_array);
	case A_delayed:
	case A_stable:
	case A_quiet:
	case A_transaction:
	case A_event:
	case A_active:
	case A_last_event:
	case A_last_active:
	case A_last_value:
	case A_driving:
	case A_driving_value:
		// the prefix must be static signal name.
		if (!prefix->isRight(OP_attribute) || !isSignalKind(prefix))
			if (prefix->get_object_type() != O_signal)
				return 0;
		return isStaticName(prefix, 1);
	case A_simple_name:
	case A_instance_name:
	case A_path_name:
		return prefix->hasName();
	}
	return 0;
}

int isAttributeParameter(oid obj, oid expr)
{
	if (!obj || !obj->isRight(OP_attribute)) {
		pi_error(Error, "isAttributeParameter()", "illegal appearance.");
		return 1;
	}

	if (obj->get_attr_kind() == A_user)
		return expr ? 0:1;

	switch (obj->get_attr_kind()) {
	case A_base:
	case A_left:
	case A_right:
	case A_high:
	case A_low:
	case A_ascending:
		return expr ? 0:1;
	case A_image:
	case A_value:
	case A_pos:
	case A_val:
	case A_succ:
	case A_pred:
	case A_leftof:
	case A_rightof:
		return expr ? 1:0;
	case A_left_:
	case A_right_:
	case A_high_:
	case A_low_:
	case A_range:
	case A_reverse_range:
	case A_length:
	case A_ascending_:
	case A_delayed:
	case A_stable:
	case A_quiet:
		return expr ? 1:1;
	case A_transaction:
	case A_event:
	case A_active:
	case A_last_event:
	case A_last_active:
	case A_last_value:
	case A_driving:
	case A_driving_value:
	case A_simple_name:
	case A_instance_name:
	case A_path_name:
		return expr ? 0:1;
	}
	return 0;
}

