<commit_before>/* Copyright 2015 Codethink Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <ofc/sema.h>


ofc_sema_decl_t* ofc_sema_decl_create(
	const ofc_sema_type_t* type,
	ofc_str_ref_t name)
{
	ofc_sema_decl_t* decl
		= (ofc_sema_decl_t*)malloc(
			sizeof(ofc_sema_decl_t));
	if (!decl) return NULL;

	decl->type = type;
	decl->name = name;
	decl->func = NULL;

	if (ofc_sema_type_is_composite(type))
	{
		decl->init_array = NULL;
	}
	else
	{
		decl->init = NULL;
	}

	decl->equiv = NULL;

	decl->is_static    = false;
	decl->is_volatile  = false;
	decl->is_automatic = false;
	decl->is_target    = false;

	decl->used = false;
	return decl;
}

static ofc_sema_decl_t* ofc_sema_decl__spec(
	ofc_sema_scope_t*       scope,
	ofc_str_ref_t           name,
	const ofc_sema_spec_t*  spec,
	const ofc_sema_array_t* array,
	bool                    is_procedure,
	bool                    is_function)
{
	if (!spec)
		return NULL;

	if (spec->common && (spec->is_static
		|| spec->is_automatic || spec->is_volatile
		|| spec->is_intrinsic || spec->is_external))
	{
		/* TODO - Work out if VOLATILE should be allowed. */
		ofc_sema_scope_error(scope, name,
			"COMMON block entries can't have declaration attributes.");
		return NULL;
	}

	if (is_procedure)
	{
		if (spec->is_static)
		{
			ofc_sema_scope_error(scope, name,
				"Procedures cannot be STATIC");
			return NULL;
		}

		if (spec->is_automatic)
		{
			ofc_sema_scope_error(scope, name,
				"Procedures cannot be AUTOMATIC");
			return NULL;
		}

		if (spec->is_volatile)
		{
			ofc_sema_scope_error(scope, name,
				"Procedures cannot be VOLATILE");
			return NULL;
		}

		if (spec->is_intrinsic
			&& spec->is_external)
		{
			ofc_sema_scope_error(scope, name,
				"A procedure cannot be INTRINSIC and EXTERNAL");
			return NULL;
		}

		if (!is_function)
		{
			if (!spec->type_implicit
				|| (spec->kind > 0)
				|| (spec->len > 0)
				|| spec->len_var)
			{
				ofc_sema_scope_error(scope, name,
					"A SUBROUTINE cannot have a specified type");
				return NULL;
			}
		}
	}
	else
	{
		if (spec->is_intrinsic)
		{
			ofc_sema_scope_error(scope, name,
				"Only procedures may be INTRINSIC");
			return NULL;
		}

		if (spec->is_external)
		{
			ofc_sema_scope_error(scope, name,
				"Only procedures may be EXTERNAL");
			return NULL;
		}

		if (spec->is_static
			&& spec->is_automatic)
		{
			ofc_sema_scope_error(scope, name,
				"Declaration cannot be AUTOMATIC and STATIC");
			return NULL;
		}
	}

	if (spec->array)
	{
		if (array)
		{
			if (!ofc_sema_array_compare(
				array, spec->array))
			{
				ofc_sema_scope_error(scope, name,
					"Conflicting array dimensions in declaration");
				return NULL;
			}

			ofc_sema_scope_warning(scope, name,
				"Repetition of array dimensions in declaration");
		}

		array = NULL;
	}

	const ofc_sema_type_t* type
		= ofc_sema_type_spec(spec);
	if (!type) return NULL;

	if (array)
	{
		type = ofc_sema_type_create_array(type, array);
		if (!type) return NULL;
	}

	if (is_procedure)
	{
		type = (is_function
			? ofc_sema_type_create_function(type)
			: ofc_sema_type_subroutine());
		if (!type) return NULL;
	}

	ofc_sema_decl_t* decl
		= ofc_sema_decl_create(type, name);
	if (!decl) return NULL;

	decl->is_static    = spec->is_static;
	decl->is_automatic = spec->is_automatic;
	decl->is_volatile  = spec->is_volatile;
	decl->is_intrinsic = spec->is_intrinsic;
	decl->is_external  = spec->is_external;

	if (!ofc_sema_decl_list_add(
		scope->decl, decl))
	{
		ofc_sema_decl_delete(decl);
		return NULL;
	}

	if (spec->common)
	{
		if (!ofc_sema_common_define(
			spec->common, spec->common_offset, decl))
		{
			/* This should never happen. */
			abort();
		}
	}

	return decl;
}

ofc_sema_decl_t* ofc_sema_decl_spec(
	ofc_sema_scope_t*       scope,
	ofc_str_ref_t           name,
	const ofc_sema_spec_t*  spec,
	const ofc_sema_array_t* array)
{
	return ofc_sema_decl__spec(
		scope, name, spec, array, false, false);
}

ofc_sema_decl_t* ofc_sema_decl_implicit(
	ofc_sema_scope_t*       scope,
	ofc_str_ref_t           name,
	const ofc_sema_array_t* array)
{
	ofc_sema_spec_t* spec
		= ofc_sema_scope_spec_find_final(scope, name);
	if (!spec) return NULL;

	ofc_sema_decl_t* decl;
	if (spec->is_external
		|| spec->is_intrinsic)
	{
		if (spec->type_implicit)
		{
			decl = ofc_sema_decl_subroutine(
				scope, name);
		}
		else
		{
			decl = ofc_sema_decl_function(
				scope, name, spec);
		}
	}
	else
	{
		decl = ofc_sema_decl_spec(
			scope, name, spec, array);
	}

	ofc_sema_spec_delete(spec);
	return decl;
}

ofc_sema_decl_t* ofc_sema_decl_implicit_lhs(
	ofc_sema_scope_t*       scope,
	const ofc_parse_lhs_t*  lhs)
{
	if (!scope || !lhs)
		return NULL;

	ofc_sema_array_t* array = NULL;
	ofc_str_ref_t base_name;
	if (!ofc_parse_lhs_base_name(
		*lhs, &base_name))
		return NULL;

    switch (lhs->type)
	{
		case OFC_PARSE_LHS_VARIABLE:
			break;
		case OFC_PARSE_LHS_ARRAY:
			array = ofc_sema_array(
				scope, lhs->array.index);
			if (!array) return NULL;
			break;
		default:
			break;
	}

	ofc_sema_decl_t* decl
		= ofc_sema_decl_implicit(
			scope, base_name, array);
	ofc_sema_array_delete(array);
	return decl;
}


ofc_sema_decl_t* ofc_sema_decl_function(
	ofc_sema_scope_t*       scope,
	ofc_str_ref_t           name,
	const ofc_sema_spec_t*  spec)
{
	return ofc_sema_decl__spec(
		scope, name, spec, NULL, true, true);
}

ofc_sema_decl_t* ofc_sema_decl_subroutine(
	ofc_sema_scope_t* scope,
	ofc_str_ref_t     name)
{
	ofc_sema_spec_t* spec
		= ofc_sema_scope_spec_find_final(
			scope, name);
	ofc_sema_decl_t* decl = ofc_sema_decl__spec(
		scope, name, spec, NULL, true, false);
	ofc_sema_spec_delete(spec);
	return decl;
}


static bool ofc_sema_decl__decl(
	ofc_sema_scope_t*       scope,
	ofc_sema_spec_t         spec,
	const ofc_parse_decl_t* pdecl)
{
	if (!pdecl || !pdecl->lhs)
		return false;

	bool is_decl = (pdecl->init_clist || pdecl->init_expr);

	const ofc_parse_lhs_t* lhs = pdecl->lhs;
	if (lhs->type == OFC_PARSE_LHS_STAR_LEN)
	{
		if (lhs->star_len.var)
		{
			if (spec.len > 0)
			{
				ofc_sema_scope_warning(scope, lhs->src,
					"Overriding specified star length in %s list",
					(is_decl ? "decl" : "specifier"));
			}

			spec.len     = 0;
			spec.len_var = true;
		}
		else
		{
			ofc_sema_expr_t* expr
				= ofc_sema_expr(scope, lhs->star_len.len);
			if (!expr) return false;

			unsigned star_len;
			bool resolved = ofc_sema_expr_resolve_uint(expr, &star_len);
			ofc_sema_expr_delete(expr);

			if (!resolved)
			{
				ofc_sema_scope_error(scope, lhs->src,
					"Star length must be a positive whole integer");
				return false;
			}

			if (spec.len_var
				|| ((spec.len > 0) && (spec.len != star_len)))
			{
				ofc_sema_scope_warning(scope, lhs->src,
					"Overriding specified star length in %s list",
					(is_decl ? "decl" : "specifier"));
			}

			spec.len     = star_len;
			spec.len_var = false;
		}

		lhs = lhs->parent;
		if (!lhs) return false;
	}

	if (lhs->type == OFC_PARSE_LHS_ARRAY)
	{
		if (spec.array)
		{
			/* This shouldn't ever happen since arrays
			   can't be specified on the LHS of a declaration. */
			return false;
		}

		spec.array = ofc_sema_array(
			scope, lhs->array.index);
		if (!spec.array) return false;

		lhs = lhs->parent;
		if (!lhs)
		{
			ofc_sema_array_delete(spec.array);
			return false;
		}
	}
	else if (spec.array)
	{
		spec.array = ofc_sema_array_copy(spec.array);
		if (!spec.array) return false;
	}

	if (lhs->type != OFC_PARSE_LHS_VARIABLE)
	{
		ofc_sema_array_delete(spec.array);
		return false;
	}

	if (!ofc_parse_lhs_base_name(
		*lhs, &spec.name))
	{
		ofc_sema_array_delete(spec.array);
		return false;
	}

	ofc_sema_decl_t* decl
		= ofc_sema_scope_decl_find_modify(
			scope, spec.name, true);
	bool exist = (decl != NULL);

	if (is_decl)
	{
		if (exist)
		{
			/* TODO - Handle redeclarations which match original. */

			ofc_sema_scope_error(scope, lhs->src,
				"Redeclaration of declared variable");
			ofc_sema_array_delete(spec.array);
			return false;
		}

		decl = ofc_sema_decl__spec(
			scope, spec.name, &spec,
			NULL, false, false);
		ofc_sema_array_delete(spec.array);
		if (!decl) return false;

		if (pdecl->init_expr)
		{
			ofc_sema_expr_t* init_expr
				= ofc_sema_expr(scope, pdecl->init_expr);
			if (!init_expr)
				return false;

			bool initialized = ofc_sema_decl_init(
				scope, decl, init_expr);
			ofc_sema_expr_delete(init_expr);

			if (!initialized)
				return false;
		}
		else if (pdecl->init_clist)
		{
			/* TODO - CList initializer resolution. */
			return false;
		}
	}
	else
	{
		if (exist)
		{
			/* TODO - Handle specifications which are compatible. */

			ofc_sema_scope_error(scope, lhs->src,
				"Specification of declared variable");
			ofc_sema_array_delete(spec.array);
			return false;
		}

		ofc_sema_spec_t* nspec
			= ofc_sema_scope_spec_modify(
				scope, spec.name);<commit_msg>Print an error for CList initializers as we don't yet support them<commit_after>/* Copyright 2015 Codethink Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <ofc/sema.h>


ofc_sema_decl_t* ofc_sema_decl_create(
	const ofc_sema_type_t* type,
	ofc_str_ref_t name)
{
	ofc_sema_decl_t* decl
		= (ofc_sema_decl_t*)malloc(
			sizeof(ofc_sema_decl_t));
	if (!decl) return NULL;

	decl->type = type;
	decl->name = name;
	decl->func = NULL;

	if (ofc_sema_type_is_composite(type))
	{
		decl->init_array = NULL;
	}
	else
	{
		decl->init = NULL;
	}

	decl->equiv = NULL;

	decl->is_static    = false;
	decl->is_volatile  = false;
	decl->is_automatic = false;
	decl->is_target    = false;

	decl->used = false;
	return decl;
}

static ofc_sema_decl_t* ofc_sema_decl__spec(
	ofc_sema_scope_t*       scope,
	ofc_str_ref_t           name,
	const ofc_sema_spec_t*  spec,
	const ofc_sema_array_t* array,
	bool                    is_procedure,
	bool                    is_function)
{
	if (!spec)
		return NULL;

	if (spec->common && (spec->is_static
		|| spec->is_automatic || spec->is_volatile
		|| spec->is_intrinsic || spec->is_external))
	{
		/* TODO - Work out if VOLATILE should be allowed. */
		ofc_sema_scope_error(scope, name,
			"COMMON block entries can't have declaration attributes.");
		return NULL;
	}

	if (is_procedure)
	{
		if (spec->is_static)
		{
			ofc_sema_scope_error(scope, name,
				"Procedures cannot be STATIC");
			return NULL;
		}

		if (spec->is_automatic)
		{
			ofc_sema_scope_error(scope, name,
				"Procedures cannot be AUTOMATIC");
			return NULL;
		}

		if (spec->is_volatile)
		{
			ofc_sema_scope_error(scope, name,
				"Procedures cannot be VOLATILE");
			return NULL;
		}

		if (spec->is_intrinsic
			&& spec->is_external)
		{
			ofc_sema_scope_error(scope, name,
				"A procedure cannot be INTRINSIC and EXTERNAL");
			return NULL;
		}

		if (!is_function)
		{
			if (!spec->type_implicit
				|| (spec->kind > 0)
				|| (spec->len > 0)
				|| spec->len_var)
			{
				ofc_sema_scope_error(scope, name,
					"A SUBROUTINE cannot have a specified type");
				return NULL;
			}
		}
	}
	else
	{
		if (spec->is_intrinsic)
		{
			ofc_sema_scope_error(scope, name,
				"Only procedures may be INTRINSIC");
			return NULL;
		}

		if (spec->is_external)
		{
			ofc_sema_scope_error(scope, name,
				"Only procedures may be EXTERNAL");
			return NULL;
		}

		if (spec->is_static
			&& spec->is_automatic)
		{
			ofc_sema_scope_error(scope, name,
				"Declaration cannot be AUTOMATIC and STATIC");
			return NULL;
		}
	}

	if (spec->array)
	{
		if (array)
		{
			if (!ofc_sema_array_compare(
				array, spec->array))
			{
				ofc_sema_scope_error(scope, name,
					"Conflicting array dimensions in declaration");
				return NULL;
			}

			ofc_sema_scope_warning(scope, name,
				"Repetition of array dimensions in declaration");
		}

		array = NULL;
	}

	const ofc_sema_type_t* type
		= ofc_sema_type_spec(spec);
	if (!type) return NULL;

	if (array)
	{
		type = ofc_sema_type_create_array(type, array);
		if (!type) return NULL;
	}

	if (is_procedure)
	{
		type = (is_function
			? ofc_sema_type_create_function(type)
			: ofc_sema_type_subroutine());
		if (!type) return NULL;
	}

	ofc_sema_decl_t* decl
		= ofc_sema_decl_create(type, name);
	if (!decl) return NULL;

	decl->is_static    = spec->is_static;
	decl->is_automatic = spec->is_automatic;
	decl->is_volatile  = spec->is_volatile;
	decl->is_intrinsic = spec->is_intrinsic;
	decl->is_external  = spec->is_external;

	if (!ofc_sema_decl_list_add(
		scope->decl, decl))
	{
		ofc_sema_decl_delete(decl);
		return NULL;
	}

	if (spec->common)
	{
		if (!ofc_sema_common_define(
			spec->common, spec->common_offset, decl))
		{
			/* This should never happen. */
			abort();
		}
	}

	return decl;
}

ofc_sema_decl_t* ofc_sema_decl_spec(
	ofc_sema_scope_t*       scope,
	ofc_str_ref_t           name,
	const ofc_sema_spec_t*  spec,
	const ofc_sema_array_t* array)
{
	return ofc_sema_decl__spec(
		scope, name, spec, array, false, false);
}

ofc_sema_decl_t* ofc_sema_decl_implicit(
	ofc_sema_scope_t*       scope,
	ofc_str_ref_t           name,
	const ofc_sema_array_t* array)
{
	ofc_sema_spec_t* spec
		= ofc_sema_scope_spec_find_final(scope, name);
	if (!spec) return NULL;

	ofc_sema_decl_t* decl;
	if (spec->is_external
		|| spec->is_intrinsic)
	{
		if (spec->type_implicit)
		{
			decl = ofc_sema_decl_subroutine(
				scope, name);
		}
		else
		{
			decl = ofc_sema_decl_function(
				scope, name, spec);
		}
	}
	else
	{
		decl = ofc_sema_decl_spec(
			scope, name, spec, array);
	}

	ofc_sema_spec_delete(spec);
	return decl;
}

ofc_sema_decl_t* ofc_sema_decl_implicit_lhs(
	ofc_sema_scope_t*       scope,
	const ofc_parse_lhs_t*  lhs)
{
	if (!scope || !lhs)
		return NULL;

	ofc_sema_array_t* array = NULL;
	ofc_str_ref_t base_name;
	if (!ofc_parse_lhs_base_name(
		*lhs, &base_name))
		return NULL;

    switch (lhs->type)
	{
		case OFC_PARSE_LHS_VARIABLE:
			break;
		case OFC_PARSE_LHS_ARRAY:
			array = ofc_sema_array(
				scope, lhs->array.index);
			if (!array) return NULL;
			break;
		default:
			break;
	}

	ofc_sema_decl_t* decl
		= ofc_sema_decl_implicit(
			scope, base_name, array);
	ofc_sema_array_delete(array);
	return decl;
}


ofc_sema_decl_t* ofc_sema_decl_function(
	ofc_sema_scope_t*       scope,
	ofc_str_ref_t           name,
	const ofc_sema_spec_t*  spec)
{
	return ofc_sema_decl__spec(
		scope, name, spec, NULL, true, true);
}

ofc_sema_decl_t* ofc_sema_decl_subroutine(
	ofc_sema_scope_t* scope,
	ofc_str_ref_t     name)
{
	ofc_sema_spec_t* spec
		= ofc_sema_scope_spec_find_final(
			scope, name);
	ofc_sema_decl_t* decl = ofc_sema_decl__spec(
		scope, name, spec, NULL, true, false);
	ofc_sema_spec_delete(spec);
	return decl;
}


static bool ofc_sema_decl__decl(
	ofc_sema_scope_t*       scope,
	ofc_sema_spec_t         spec,
	const ofc_parse_decl_t* pdecl)
{
	if (!pdecl || !pdecl->lhs)
		return false;

	bool is_decl = (pdecl->init_clist || pdecl->init_expr);

	const ofc_parse_lhs_t* lhs = pdecl->lhs;
	if (lhs->type == OFC_PARSE_LHS_STAR_LEN)
	{
		if (lhs->star_len.var)
		{
			if (spec.len > 0)
			{
				ofc_sema_scope_warning(scope, lhs->src,
					"Overriding specified star length in %s list",
					(is_decl ? "decl" : "specifier"));
			}

			spec.len     = 0;
			spec.len_var = true;
		}
		else
		{
			ofc_sema_expr_t* expr
				= ofc_sema_expr(scope, lhs->star_len.len);
			if (!expr) return false;

			unsigned star_len;
			bool resolved = ofc_sema_expr_resolve_uint(expr, &star_len);
			ofc_sema_expr_delete(expr);

			if (!resolved)
			{
				ofc_sema_scope_error(scope, lhs->src,
					"Star length must be a positive whole integer");
				return false;
			}

			if (spec.len_var
				|| ((spec.len > 0) && (spec.len != star_len)))
			{
				ofc_sema_scope_warning(scope, lhs->src,
					"Overriding specified star length in %s list",
					(is_decl ? "decl" : "specifier"));
			}

			spec.len     = star_len;
			spec.len_var = false;
		}

		lhs = lhs->parent;
		if (!lhs) return false;
	}

	if (lhs->type == OFC_PARSE_LHS_ARRAY)
	{
		if (spec.array)
		{
			/* This shouldn't ever happen since arrays
			   can't be specified on the LHS of a declaration. */
			return false;
		}

		spec.array = ofc_sema_array(
			scope, lhs->array.index);
		if (!spec.array) return false;

		lhs = lhs->parent;
		if (!lhs)
		{
			ofc_sema_array_delete(spec.array);
			return false;
		}
	}
	else if (spec.array)
	{
		spec.array = ofc_sema_array_copy(spec.array);
		if (!spec.array) return false;
	}

	if (lhs->type != OFC_PARSE_LHS_VARIABLE)
	{
		ofc_sema_array_delete(spec.array);
		return false;
	}

	if (!ofc_parse_lhs_base_name(
		*lhs, &spec.name))
	{
		ofc_sema_array_delete(spec.array);
		return false;
	}

	ofc_sema_decl_t* decl
		= ofc_sema_scope_decl_find_modify(
			scope, spec.name, true);
	bool exist = (decl != NULL);

	if (is_decl)
	{
		if (exist)
		{
			/* TODO - Handle redeclarations which match original. */

			ofc_sema_scope_error(scope, lhs->src,
				"Redeclaration of declared variable");
			ofc_sema_array_delete(spec.array);
			return false;
		}

		decl = ofc_sema_decl__spec(
			scope, spec.name, &spec,
			NULL, false, false);
		ofc_sema_array_delete(spec.array);
		if (!decl) return false;

		if (pdecl->init_expr)
		{
			ofc_sema_expr_t* init_expr
				= ofc_sema_expr(scope, pdecl->init_expr);
			if (!init_expr)
				return false;

			bool initialized = ofc_sema_decl_init(
				scope, decl, init_expr);
			ofc_sema_expr_delete(init_expr);

			if (!initialized)
				return false;
		}
		else if (pdecl->init_clist)
		{
			ofc_sema_scope_error(scope, lhs->src,
				"CList initializers not yet supported");
			/* TODO - CList initializer resolution. */
			return false;
		}
	}
	else
	{
		if (exist)
		{
			/* TODO - Handle specifications which are compatible. */

			ofc_sema_scope_error(scope, lhs->src,
				"Specification of declared variable");
			ofc_sema_array_delete(spec.array);
			return false;
		}

		ofc_sema_spec_t* nspec
			= ofc_sema_scope_spec_modify(
				scope, spec.name);
{'fraction_non_alphanumeric': 0.11500449236298294, 'fraction_numerical': 0.001374134559484171, 'mean_word_length': 3.4055878928987195, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 156, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '457192', 'n_tokens_mistral': 8645, 'n_tokens_neox': 7662, 'n_words': 1593}