pascalabcnet/TreeConverter/TreeConversion/SyntaxTreeVisitorNodes/Patterns.cs

321 lines
15 KiB
C#
Raw Normal View History

2018-05-01 19:29:39 +03:00
using System;
using System.Collections.Generic;
2018-05-09 15:31:50 +03:00
using System.Diagnostics;
2018-05-01 19:29:39 +03:00
using System.Linq;
using System.Text;
2018-05-06 20:09:36 +03:00
using PascalABCCompiler.SyntaxTree;
2018-05-01 19:29:39 +03:00
using PascalABCCompiler.TreeRealization;
namespace PascalABCCompiler.TreeConverter
{
2018-05-11 13:46:59 +03:00
// Patterns
2018-05-01 19:29:39 +03:00
public partial class syntax_tree_visitor
{
2018-05-06 20:09:36 +03:00
public override void visit(desugared_deconstruction deconstruction)
{
var invokationTarget = convert_strong(deconstruction.deconstruction_target as expression);
2018-05-17 22:49:07 +03:00
var types = InferAndCheckPatternVariableTypes(deconstruction.variables.definitions, invokationTarget, deconstruction);
2018-05-06 20:09:36 +03:00
if (types == null)
return;
foreach (var definition in deconstruction.WithTypes(types.Select(x => new semantic_type_node(x)).ToArray()))
definition.visit(this);
}
private type_node[] InferAndCheckPatternVariableTypes(List<var_def_statement> variableDefinitions, expression_node patternInstance, desugared_deconstruction deconstruction)
2018-05-06 20:09:36 +03:00
{
2018-10-20 11:20:53 +03:00
if (patternInstance.type.IsPointer)
{
AddError(get_location(deconstruction), "PATTERN_MATCHING_DOESNT_SUPPORT_POINTERS");
return null;
}
2018-05-09 15:31:50 +03:00
var parameterTypes = variableDefinitions.Select(x => x.vars_type == null ? null : convert_strong(x.vars_type)).ToArray();
List<function_node> candidates = new List<function_node>();
List<type_node[]> deducedParametersList = new List<type_node[]>();
var allDeconstructs = patternInstance.type.find_in_type(compiler_string_consts.deconstruct_method_name, context.CurrentScope);
2018-10-20 11:08:27 +03:00
if (allDeconstructs == null)
{
2018-10-20 11:20:53 +03:00
AddError(get_location(deconstruction), "NO_DECONSTRUCT_FOUND");
2018-10-20 11:08:27 +03:00
return null;
}
2018-05-11 14:11:56 +03:00
foreach (var canditateSymbol in allDeconstructs)
2018-05-09 15:31:50 +03:00
{
var deducedParameters = new type_node[parameterTypes.Length];
2018-05-09 15:31:50 +03:00
var possibleCandidate = canditateSymbol.sym_info as function_node;
if (!IsSuitableFunction(possibleCandidate, parameterTypes, patternInstance, get_location(deconstruction), out deducedParameters))
2018-05-09 15:31:50 +03:00
continue;
deducedParametersList.Add(deducedParameters);
2018-05-09 15:31:50 +03:00
candidates.Add(possibleCandidate);
}
if (candidates.Count == 0)
{
AddError(get_location(deconstruction), "NO_SUITABLE_DECONSTRUCT_FOUND");
return null;
}
else
if (candidates.Count > 1)
{
RemoveDefaultDeconstruct(candidates);
if (candidates.Count > 1 && !CheckIfParameterListElementsAreTheSame(deducedParametersList))
2018-05-09 15:31:50 +03:00
{
AddError(get_location(deconstruction), "DECONSTRUCTOR_METHOD_AMBIGUITY");
return null;
}
}
// Единственный подхдящий кандидат найден, либо их несколько, с одинаковыми выходными параметрами
var chosenFunction = candidates.First();
if (chosenFunction.is_extension_method)
2018-05-09 15:31:50 +03:00
{
if (chosenFunction.is_generic_function)
chosenFunction = generic_convertions.get_function_instance(chosenFunction, deducedParametersList.First().ToList());
2018-05-09 15:31:50 +03:00
return chosenFunction.parameters.Where(x => !IsSelfParameter(x)).Select(x => x.type).ToArray();
}
else
return chosenFunction.parameters.Select(x => x.type).ToArray();//deducedParametersList[0];
2018-05-09 15:31:50 +03:00
}
/// <summary>
2020-06-23 00:35:39 +03:00
/// Проверяет, подходит ли функция для вызова с указанными параметрами
2018-05-09 15:31:50 +03:00
/// </summary>
/// <param name="candidate"></param>
/// <param name="givenParameterTypes">Типы параметров, указанные пользователем</param>
2018-05-09 15:31:50 +03:00
/// <returns></returns>
private bool IsSuitableFunction(
function_node candidate,
type_node[] givenParameterTypes,
expression_node patternInstance,
location deconstructionLocation,
out type_node[] parameterTypes)
2018-05-09 15:31:50 +03:00
{
parameterTypes = new type_node[givenParameterTypes.Length];
2023-04-07 15:09:53 +03:00
if (candidate.parameters.Count == 0)
return false;
2020-06-23 00:35:39 +03:00
candidate.parameters[0].name = "Self"; // SSM 23.06.20 #2268 - это если в NET где-то такое нашли
var selfParameter = candidate.is_extension_method ? candidate.parameters.FirstOrDefault(IsSelfParameter) : null;
2018-05-11 19:26:55 +03:00
Debug.Assert(!candidate.is_extension_method || selfParameter != null, "Couldn't find self parameter in extension method");
2018-05-09 15:31:50 +03:00
var candidateParameterTypes =
candidate.is_extension_method ?
candidate.parameters.Where(x => !IsSelfParameter(x)).ToArray() :
candidate.parameters.ToArray();
if (candidateParameterTypes.Length != givenParameterTypes.Length)
2018-05-09 15:31:50 +03:00
return false;
// Разрешаем только deconstruct текущего класса, родительские в расчет не берем
if (candidate is common_method_node commonMethod && !AreTheSameType(patternInstance.type, commonMethod.cont_type))
return false;
var genericDeduceNeeded = candidate.is_extension_method && candidate.is_generic_function;
type_node[] deducedGenerics = new type_node[candidate.generic_parameters_count];
if (genericDeduceNeeded)
{
// Выводим дженерики по self
var nils = new List<int>();
2020-06-13 15:35:47 +03:00
var deduceSucceded = generic_convertions.DeduceInstanceTypes(selfParameter.type, patternInstance.type, deducedGenerics, nils, candidate.get_generic_params_list());
if (!deduceSucceded || deducedGenerics.Contains(null))
return false;
}
for (int i = 0; i < givenParameterTypes.Length; i++)
2018-05-09 15:31:50 +03:00
{
var givenParameter = givenParameterTypes[i];
var candidateParameter = candidateParameterTypes[i].type;
if (genericDeduceNeeded && (candidateParameter.is_generic_parameter || candidateParameter.is_generic_type_instance))
2018-05-11 19:26:55 +03:00
candidateParameter = InstantiateParameter(candidateParameter, deducedGenerics);
2021-12-24 17:38:39 +03:00
if (givenParameter != null && !AreTheSameType(candidateParameter, givenParameter)
&& !(candidateParameter != null && givenParameter != null && candidateParameter.is_generic_parameter
&& givenParameter.is_generic_parameter && candidateParameter.name == givenParameter.name && givenParameter.base_type == candidateParameter.base_type))
2018-05-09 15:31:50 +03:00
return false;
parameterTypes[i] = candidateParameter;
2018-05-09 15:31:50 +03:00
}
return true;
}
private type_node GetDeconstructorOwner(function_node deconstructor)
2018-05-09 15:31:50 +03:00
{
switch (deconstructor)
{
case common_method_node commonMethod: return commonMethod.cont_type;
case common_namespace_function_node commonNamespaseFunction: return GetSelfParameter(commonNamespaseFunction).type;
default: return null;
}
2018-05-09 15:31:50 +03:00
}
private bool AreTheSameType(type_node type1, type_node type2) =>
type1 != null &&
type2 != null &&
convertion_data_and_alghoritms.possible_equal_types(type1, type2);
2018-05-09 15:31:50 +03:00
private bool IsSelfParameter(parameter parameter) => parameter.name.ToLower() == compiler_string_consts.self_word;
private void RemoveDefaultDeconstruct(List<function_node> candidates)
{
var index = candidates.FindIndex(IsDefaultDeconstruct);
if (index >= 0)
candidates.RemoveAt(index);
}
private bool IsDefaultDeconstruct(function_node function)
2018-05-09 15:31:50 +03:00
{
// TODO Patterns: check if it declared in PABCSystem
2018-05-09 15:31:50 +03:00
return
function.generic_parameters_count == 1 &&
function.parameters.Count == 2 &&
function.parameters[0].type.is_generic_parameter &&
function.parameters[1].type.is_generic_parameter &&
2018-05-18 18:18:38 +03:00
AreTheSameType(function.parameters[0].type, function.parameters[1].type);
2018-05-09 15:31:50 +03:00
}
private bool CheckIfParameterListElementsAreTheSame(List<type_node[]> parametersList)
2018-05-09 15:31:50 +03:00
{
var first = parametersList.First();
for (int i = 1; i < parametersList.Count; i++)
{
if (first.Length != parametersList[i].Length)
return false;
for (int j = 0; j < first.Length; j++)
2018-05-18 18:18:38 +03:00
if (!AreTheSameType(first[j], parametersList[i][j]))
return false;
}
return true;
}
private type_node InstantiateParameter(SemanticTree.ITypeNode genericType, type_node[] instances)
{
if (genericType.is_generic_parameter)
return instances[(genericType as type_node).generic_param_index];
else
{
var genericNode = (genericType as generic_instance_type_node);
if (genericNode != null)
{
var parameters = genericNode.instance_params.Select(x => InstantiateParameter(x, instances)).ToList();
genericNode.instance_params = parameters;
}
return genericNode as type_node;
}
2018-05-06 20:09:36 +03:00
}
2018-05-18 18:18:38 +03:00
private IEnumerable<int> GenericParameterIndices(SemanticTree.ITypeNode node)
{
if (node.is_generic_parameter)
yield return (node as type_node).generic_param_index;
if (node is generic_instance_type_node genericNode)
foreach (var index in genericNode.instance_params.Aggregate(
Enumerable.Empty<int>(),
2018-05-18 18:18:38 +03:00
(indices, nextNode) => indices.Concat(GenericParameterIndices(nextNode)))
.Distinct())
yield return index;
yield break;
}
private parameter GetSelfParameter(function_node node) => node.parameters.FirstOrDefault(x => IsSelfParameter(x));
/// <summary>
/// Проводит проверки, общие для экземплярных деконструкторов и расширений
/// </summary>
/// <param name="deconstructor"></param>
private void ExecuteCommonChecks(common_function_node deconstructor)
{
var dd = deconstructor as common_method_node;
if (dd != null && dd.IsStatic)
AddError(deconstructor.loc, "DECONSTRUCTOR_SHOULD_NOT_BE_STATIC");
2018-05-18 18:18:38 +03:00
if (deconstructor.return_value_type != null)
AddError(deconstructor.loc, "DECONSTRUCTOR_SHOULD_BE_A_PROCEDURE");
2018-05-18 18:18:38 +03:00
foreach (var parameter in deconstructor.parameters.Where(x => !IsSelfParameter(x)))
if (parameter.parameter_type != SemanticTree.parameter_type.var && parameter is common_parameter p)
AddError(p.loc, "DECONSTRUCTION_PARAMETERS_SHOULD_HAVE_VAR_MODIFIER");
}
2018-06-17 14:21:52 +03:00
/// <summary>
/// Проверяет выражение, сопоставляемое с образцом
/// </summary>
/// <param name="matchedExpression"></param>
private void CheckMatchedExpression(expression matchedExpression)
{
var convertedExpression = convert_strong(matchedExpression);
if (convertedExpression.type.IsPointer)
AddError(get_location(matchedExpression), "PATTERN_MATCHING_DOESNT_SUPPORT_POINTERS");
}
private void CheckIfCanBeMatched(expression matchedExpression, type_definition targetType)
{
var type = convert_strong(targetType);
2020-03-09 12:37:13 +03:00
var expression = convert_strong(matchedExpression);
if (expression is typed_expression)
try_convert_typed_expression_to_function_call(ref expression);
var expressionType = expression.type;
if (type_table.is_derived(type, expressionType) ||
type_table.is_derived(expressionType, type) ||
AreTheSameType(type, expressionType) ||
2018-10-24 23:55:52 +03:00
type.IsInterface ||
2020-10-07 18:41:43 +03:00
expressionType.IsInterface || type.is_generic_parameter || expressionType.is_generic_parameter)
return;
2020-03-09 12:37:13 +03:00
AddError(get_location(matchedExpression), "EXPRESSION_OF_TYPE_{0}_CANNOT_BE_MATCHED_AGAINST_PATTERN_WITH_TYPE_{1}", expressionType.name, type.name);
}
private void CheckIfCanBeMatched(expression matchedExpression, expression patternExpression)
{
2019-05-25 09:35:52 +03:00
var patternNode = convert_strong(patternExpression);
var patternType = patternNode.type;
2019-05-25 12:30:15 +03:00
var expressionNode = convert_strong(matchedExpression);
2020-03-09 12:37:13 +03:00
if (expressionNode is typed_expression)
try_convert_typed_expression_to_function_call(ref expressionNode);
2019-05-25 12:30:15 +03:00
var expressionType = expressionNode.type;
2019-05-25 12:30:15 +03:00
if (!(patternNode is constant_node))
2019-05-25 09:35:52 +03:00
{
AddError(get_location(patternExpression), "MATCHING_WITH_NON_CONST");
}
2019-05-25 12:30:15 +03:00
var possible_convertion = type_table.get_convertions(patternType, expressionType);
var expressionTypeName = expressionType.BaseFullName;
var patternTypeName = patternType.BaseFullName;
var tupleName = "System.Tuple";
if (type_table.is_derived(patternType, expressionType) ||
type_table.is_derived(expressionType, patternType) ||
2019-05-25 12:30:15 +03:00
possible_convertion.first != null ||
AreTheSameType(patternType, expressionType) ||
(expressionTypeName.StartsWith(tupleName) &&
patternTypeName.StartsWith(tupleName) &&
int.Parse(expressionTypeName.Substring(tupleName.Length + 1, 1)) ==
2019-05-25 12:30:15 +03:00
int.Parse(patternTypeName.Substring(tupleName.Length + 1, 1))) ||
(expressionType.IsPointer && patternNode is null_const_node) ||
(patternType.IsPointer && expressionNode is null_const_node))
return;
AddError(get_location(matchedExpression), "EXPRESSION_OF_TYPE_{0}_CANNOT_BE_MATCHED_AGAINST_PATTERN_WITH_TYPE_{1}", expressionType.name, patternType.name);
}
private void CheckIfCanBeMatched(expression tuple, int32_const length)
{
var expressionType = convert_strong(tuple).type;
var expressionTypeName = expressionType.BaseFullName;
var tupleName = "System.Tuple";
if (expressionTypeName.StartsWith(tupleName) &&
int.Parse(expressionTypeName.Substring(tupleName.Length + 1, 1)) == length.val)
return;
AddError(get_location(tuple),
"EXPRESSION_OF_TYPE_{0}_CANNOT_BE_MATCHED_AGAINST_PATTERN_WITH_TYPE_{1}", expressionType.name, "Tuple`" + length.val);
}
2018-05-01 19:29:39 +03:00
}
}