pascalabcnet/TreeConverter/TreeConversion/LambdaHelper.cs

554 lines
28 KiB
C#
Raw Normal View History

// Copyright (c) Ivan Bondarev, Stanislav Mikhalkovich (for details please see \doc\copyright.txt)
2015-06-01 22:15:17 +03:00
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
2015-05-14 22:35:07 +03:00
using System.Collections.Generic;
using System.Text;
using System.Linq;
using PascalABCCompiler.TreeRealization;
using System.Collections;
using PascalABCCompiler.SyntaxTree;
using TreeConverter.LambdaExpressions;
namespace PascalABCCompiler.TreeConverter
{
#region Визитор поиска имен переменных (для поиска захваченных переменных в теле лямбды)
public class FindMainIdentsVisitor : WalkingVisitorNew // SSM
2015-05-14 22:35:07 +03:00
{
public ISet<ident> vars = new HashSet<ident>();
public override void visit(ident id)
{
vars.Add(id);
}
public override void visit(dot_node dn)
{
ProcessNode(dn.left);
if (dn.right.GetType()!=typeof(ident))
ProcessNode(dn.right);
}
}
#endregion
#region Визитор поиска имен локальных переменных
public class FindLocalDefsVisitor : WalkingVisitorNew // SSM
2015-05-14 22:35:07 +03:00
{
public ISet<string> vars = new HashSet<string>();
private bool indef = false;
public override void visit(ident id)
{
if (indef)
vars.Add(id.name);
}
public override void visit(SyntaxTree.var_statement defs)
{
indef = true;
ProcessNode(defs.var_def.vars); // исключаем типы -
// просматриваем только имена переменных
2015-05-14 22:35:07 +03:00
indef = false;
}
}
#endregion
public class LambdaHelper
{
public static string lambdaPrefix = "<>lambda";
private const string nonPublicMembersNamePrefix = "<>nonPublic";
private const string auxiliaryLambdaSuffix = "_$$$auxiliaryFuncName";
private const string auxVarNamePrefix = "$$$auxVar$$$<>";
private static int auxCounter = 0;
private static int nonPublicMembersNameCounter = 0;
private static int auxVarCounter = 0;
public static string GetAuxVarName()
{
return auxVarNamePrefix + auxVarCounter++;
}
public static string GetAuxiliaryLambdaName(string lambdaName)
{
return lambdaName + auxiliaryLambdaSuffix + auxCounter++;
}
public static string GetNameForNonPublicMember(string memberName)
{
return nonPublicMembersNamePrefix + memberName + nonPublicMembersNameCounter++;
}
public static string GetLambdaNamePartWithoutGenerics(string name)
{
if (!name.StartsWith(lambdaPrefix))
{
return name;
}
var ind = name.IndexOf("<", lambdaPrefix.Length);
if (ind < 0)
{
return name;
}
return name.Substring(0, ind);
}
public static void RemoveLambdaInfoFromCompilationContext(compilation_context context, function_lambda_definition lambdaDefinition)
{
if (context.converted_namespace != null
&& context.converted_namespace.functions != null
&& context.converted_namespace.functions.Any())
// Если добавилось в глобальные функции, то удаляем оттуда
{
var lastIndex = context.converted_namespace.functions.Count - 1;
if (context.converted_namespace.functions[lastIndex].name ==
lambdaDefinition.lambda_name)
{
context.converted_namespace.functions.remove_at(lastIndex);
}
}
2016-04-27 13:14:04 +03:00
//else SSM 27/04/16 - грубое исправление ошибки #147
2015-05-14 22:35:07 +03:00
{
// Если добавилась лямбда как метод класса, то удаляем оттуда
if (context.converted_type != null
&& context.converted_type.methods != null
&& context.converted_type.methods.Any())
{
var lastIndex = context.converted_type.methods.Count - 1;
if (context.converted_type.methods[lastIndex].name ==
lambdaDefinition.lambda_name)
{
context.converted_type.methods.remove_at(lastIndex);
}
}
}
}
public static type_definition ConvertSemanticTypeToSyntaxType(type_node semType)
{
2018-12-29 11:50:22 +03:00
// SSM 29/12/18 пробую скомбинировать
//if (semType is compiled_type_node && !(semType as compiled_type_node).is_generic_type_definition && !(semType as compiled_type_node).is_generic_type_instance)
//return new semantic_type_node(semType); // SSM 29/12/18 поменял на современное. Не получилось!
//else
2018-07-15 01:58:21 +03:00
return OpenMP.ConvertToSyntaxType(semType); // Это же надо! Пользоваться для этого хреновиной из OpenMP!!!
2015-05-14 22:35:07 +03:00
}
public static int processingLambdaParametersForTypeInference = 0; // Счетчик для подсчета лямбд
public static bool IsLambdaName(ident id)
{
if (id == null)
return false;
if (id.name == null)
return false;
return id.name.Contains(lambdaPrefix);
}
public static bool IsLambdaName(string id)
{
if (id == null)
return false;
return id.Contains(lambdaPrefix);
}
2018-08-09 21:34:01 +03:00
public static bool IsCapturedSelf(expression_node ex)
{
if (ex is class_field_reference)
{
class_field fld = (ex as class_field_reference).field;
if (fld.name.Contains("<>local_variables_class"))
return true;
}
return false;
}
2015-05-14 22:35:07 +03:00
public static bool IsAuxiliaryLambdaName(ident id)
{
if (id == null)
return false;
if (id.name == null)
return false;
return id.name.Contains(auxiliaryLambdaSuffix);
}
public static bool IsAuxiliaryLambdaName(string id)
{
if (id == null)
return false;
return id.Contains(auxiliaryLambdaSuffix);
}
public static Stack<KeyValuePair<string, statement_list_stack>> StatementListStackStack = new Stack<KeyValuePair<string, statement_list_stack>>();
public static void Reset()
{
CurrentLambdaScopeNum = -1;
capturedVariables = new List<SymbolInfo>();
2015-05-14 22:35:07 +03:00
captureCheck = false;
processingLambdaParametersForTypeInference = 0;
StatementListStackStack.Clear();
auxCounter = 0;
nonPublicMembersNameCounter = 0;
auxVarCounter = 0;
}
public static bool captureCheck = false;
public static List<SymbolInfo> capturedVariables = new List<SymbolInfo>();
2015-05-14 22:35:07 +03:00
public static int CurrentLambdaScopeNum = -1;
/// <summary>
/// Фиктивный блок, представляющий лямбда-выражение. Используется для обхода с целью получения списка захватываемых переменных
/// </summary>
/// <param name="lambdaDef"></param>
/// <returns></returns>
public static block CreateFictiveBlockForLambda(function_lambda_definition lambdaDef)
{
statement_list stmtList = new statement_list();
if (lambdaDef.formal_parameters != null)
for (int i = 0; i < lambdaDef.formal_parameters.params_list.Count; i++)
stmtList.subnodes.Add(SyntaxTreeNodesConstructor.CreateVarStatementNode(lambdaDef.formal_parameters.params_list[i].idents,
lambdaDef.formal_parameters.params_list[i].vars_type,
null));
if (lambdaDef.return_type != null)
stmtList.subnodes.Add(SyntaxTreeNodesConstructor.CreateVarStatementNode("result", lambdaDef.return_type, null)); // переделать, не сработает, если тип возвращаемого значения не указан
stmtList.subnodes.AddRange((lambdaDef.proc_body as statement_list).subnodes);
block resBlock = new block();
resBlock.program_code = stmtList;
return resBlock;
}
/// <summary>
/// Вывод типа возвращаемого значения лямбды
/// </summary>
public static void InferResultType(function_header funcHeader, proc_block procBody, syntax_tree_visitor visitor)
{
var retType = funcHeader.return_type as lambda_inferred_type;
if (retType != null && retType.real_type is lambda_any_type_node)
retType.real_type = new LambdaResultTypeInferrer(funcHeader, procBody, visitor).InferResultType();
}
2016-07-28 00:19:44 +03:00
/// <summary>
/// Заменяет x -> begin result := x.Print end; на x -> begin x.Print end в случае если это один оператор и вызов метода
///
/// </summary>
public static bool TryConvertFuncLambdaBodyWithMethodCallToProcLambdaBody(function_lambda_definition lambdaDef)
{
2016-12-12 21:57:41 +03:00
// SSM 12/12/16 - сделал чтобы всегда эта функция возвращала true.
// Посмотрю далее на её поведение. Мне кажется, что если мы попали сюда, то мы хотим присвоить процедурному типу,
// и в любом случае это надо интерпретировать как процедуру
2016-07-28 00:19:44 +03:00
var stl = lambdaDef.proc_body as SyntaxTree.statement_list;
if (stl.expr_lambda_body)
{
2016-12-12 21:57:41 +03:00
// Очищаем от Result := , который мы создали на предыдущем этапе
2016-07-28 00:19:44 +03:00
var ass = stl.list[0] as assign;
if (ass != null && ass.to is ident && (ass.to as ident).name.ToLower() == "result")
{
var f = ass.from;
if (f is method_call)
{
var ff = f as method_call;
stl.list[0] = new procedure_call(ff, ff.source_context); // заменить result := Print(x) на Print(x)
lambdaDef.return_type = null;
return true;
}
2016-12-12 21:57:41 +03:00
else
return false;
2016-07-28 00:19:44 +03:00
}
2019-06-27 08:56:39 +03:00
else
return false; // Т.е. это - лямбда с коротким телом, но в результате сахарных преобразований Result := переменстился не на первое место - тогда преобразовать нельзя
2016-07-28 00:19:44 +03:00
}
2016-12-12 21:57:41 +03:00
lambdaDef.return_type = null;
return true;
// return false;
2016-07-28 00:19:44 +03:00
}
2015-05-14 22:35:07 +03:00
/// <summary>
/// Вывод типа параметров лямбд и типа возвращаемого значения при присваивании лямбды переменной
/// </summary>
2021-01-06 14:11:41 +03:00
public static void InferTypesFromVarStmt(type_node leftType, function_lambda_definition lambdaDef, syntax_tree_visitor visitor, Operators op = Operators.Undefined)
2015-05-14 22:35:07 +03:00
{
if (lambdaDef == null)
return;
if (leftType != null)
{
delegate_internal_interface dii_left =
(delegate_internal_interface)leftType.get_internal_interface(internal_interface_kind.delegate_interface);
if (dii_left == null)
2017-08-10 20:58:48 +03:00
{
if (leftType != SystemLibrary.SystemLibrary.system_delegate_type)
2021-01-06 14:11:41 +03:00
{
if (op != Operators.Undefined)
{
var sil = leftType.find_in_type(name_reflector.get_name(op));
if (sil.Count > 0)
{
foreach (SymbolInfo si in sil)
{
if (si.sym_info is function_node)
{
function_node fn = si.sym_info as function_node;
if (fn.parameters.Count == 2)
{
dii_left = (delegate_internal_interface)fn.parameters[1].type.get_internal_interface(internal_interface_kind.delegate_interface);
if (dii_left != null)
break;
if (fn.parameters[1].type.is_generic_parameter)
{
compiled_type_node ctn = leftType as compiled_type_node;
common_type_node ctn2 = leftType as common_type_node;
if (ctn != null && ctn.is_generic_type_instance && fn.parameters[0].type.is_generic_type_instance && ctn.original_generic == fn.parameters[0].type.original_generic)
{
dii_left = (delegate_internal_interface)ctn.generic_params[0].get_internal_interface(internal_interface_kind.delegate_interface);
if (dii_left != null)
break;
}
if (ctn2 != null && ctn2.is_generic_type_instance && ctn2.instance_params.Count > 0 && fn.parameters[0].type.is_generic_type_instance && ctn2.original_generic == fn.parameters[0].type.original_generic)
{
dii_left = (delegate_internal_interface)ctn2.instance_params[0].get_internal_interface(internal_interface_kind.delegate_interface);
if (dii_left != null)
break;
}
}
}
}
}
}
}
if (dii_left == null)
visitor.AddError(visitor.get_location(lambdaDef), "ILLEGAL_LAMBDA_VARIABLE_TYPE");
}
2017-08-10 20:58:48 +03:00
else
return;
}
2015-05-14 22:35:07 +03:00
int leftTypeParamsNumber = dii_left.parameters.Count;
int lambdaDefParamsCount = 0;
if (lambdaDef.formal_parameters != null && lambdaDef.formal_parameters.params_list.Count != 0)
{
for (int i = 0; i < lambdaDef.formal_parameters.params_list.Count; i++)
lambdaDefParamsCount += lambdaDef.formal_parameters.params_list[i].idents.idents.Count;
if (lambdaDefParamsCount != leftTypeParamsNumber)
visitor.AddError(visitor.get_location(lambdaDef), "ILLEGAL_LAMBDA_PARAMETERS_NUMBER");
bool flag = true;
SyntaxTree.formal_parameters lambdaDefParamsTypes = new formal_parameters();
for (int i = 0; i < lambdaDef.formal_parameters.params_list.Count; i++)
for (int j = 0; j < lambdaDef.formal_parameters.params_list[i].idents.idents.Count; j++)
{
var param = new SyntaxTree.typed_parameters();
param.idents = new ident_list();
param.idents.Add(lambdaDef.formal_parameters.params_list[i].idents.idents[j]);
param.vars_type = lambdaDef.formal_parameters.params_list[i].vars_type;
2018-09-18 22:25:33 +03:00
param.source_context = lambdaDef.formal_parameters.source_context;
2015-05-14 22:35:07 +03:00
lambdaDefParamsTypes.Add(param);
}
for (int i = 0; i < leftTypeParamsNumber && flag; i++)
{
if (lambdaDefParamsTypes.params_list[i].vars_type is SyntaxTree.lambda_inferred_type)
{
if ((lambdaDefParamsTypes.params_list[i].vars_type as SyntaxTree.lambda_inferred_type).real_type is lambda_any_type_node)
{
var curLeftParType = dii_left.parameters[i].type;
lambdaDefParamsTypes.params_list[i].vars_type = new SyntaxTree.lambda_inferred_type();
(lambdaDefParamsTypes.params_list[i].vars_type as SyntaxTree.lambda_inferred_type).real_type = curLeftParType;
continue;
}
}
var lambdaPar = visitor.convert_strong(lambdaDefParamsTypes.params_list[i].vars_type);
if (!convertion_data_and_alghoritms.eq_type_nodes(dii_left.parameters[i].type, lambdaPar))
{
visitor.AddError(visitor.get_location(lambdaDef), "ILLEGAL_LAMBDA_VARIABLE_TYPE");
}
}
lambdaDef.formal_parameters = lambdaDefParamsTypes;
}
if (lambdaDef.return_type != null && lambdaDef.return_type is lambda_inferred_type)
{
if (dii_left.return_value_type != null)
(lambdaDef.return_type as lambda_inferred_type).real_type = dii_left.return_value_type;
2016-07-23 10:14:13 +03:00
else // SSM 23/07/16 - попытка бороться с var p: Shape->() := a->a.Print()
{
// lambdaDef.usedkeyword == 1 // function
var b = lambdaDef.usedkeyword == 0 && TryConvertFuncLambdaBodyWithMethodCallToProcLambdaBody(lambdaDef); // пытаться конвертировать только если мы явно не указали, что это функция
2016-07-28 00:19:44 +03:00
if (!b)
2019-06-23 14:43:27 +03:00
visitor.AddError(visitor.get_location(lambdaDef), "UNABLE_TO_CONVERT_FUNCTIONAL_TYPE_TO_PROCEDURAL_TYPE");
2016-07-23 10:14:13 +03:00
}
2015-05-14 22:35:07 +03:00
}
}
else
{
if (lambdaDef.formal_parameters != null)
for (int i = 0; i < lambdaDef.formal_parameters.params_list.Count; i++)
if (lambdaDef.formal_parameters.params_list[i].vars_type is lambda_inferred_type)
visitor.AddError(visitor.get_location(lambdaDef), "IMPOSSIBLE_TO_INFER_TYPES_IN_LAMBDA");
}
}
/// <summary>
/// Временный узел, который используется при выведении типов параметров
/// </summary>
/// <param name="def"></param>
/// <param name="visitor"></param>
/// <returns></returns>
public static typed_expression GetTempFunctionNodeForTypeInference(SyntaxTree.function_lambda_definition def, syntax_tree_visitor visitor)
{
2017-02-22 23:15:54 +03:00
var res = new common_namespace_function_node(def.lambda_name, visitor.get_location(def), null, null);
2015-05-14 22:35:07 +03:00
if (def.return_type != null)
res.return_value_type = visitor.convert_strong(def.return_type);
else
res.return_value_type = null;
res.parameters.clear();
if (def.formal_parameters != null && def.formal_parameters.params_list.Count != 0)
{
for (int i = 0; i < def.formal_parameters.params_list.Count; i++)
2018-12-29 11:50:22 +03:00
{
var tt = visitor.convert_strong(def.formal_parameters.params_list[i].vars_type); // SSM 29/12/18
2015-05-14 22:35:07 +03:00
for (int j = 0; j < def.formal_parameters.params_list[i].idents.idents.Count; j++)
{
2017-02-22 23:15:54 +03:00
var new_param = new common_parameter(null, SemanticTree.parameter_type.value, res, concrete_parameter_type.cpt_none, visitor.get_location(def.formal_parameters.params_list[i].idents.idents[0]));
2018-12-29 11:50:22 +03:00
new_param.type = tt;
2015-05-14 22:35:07 +03:00
res.parameters.AddElement(new_param);
}
2018-12-29 11:50:22 +03:00
}
2015-05-14 22:35:07 +03:00
}
var hhh = new delegated_methods();
2017-02-22 23:15:54 +03:00
hhh.proper_methods.AddElement(new common_namespace_function_call(res, visitor.get_location(def)));
return new typed_expression(hhh, visitor.get_location(def));
2015-05-14 22:35:07 +03:00
}
public static procedure_definition ConvertLambdaNodeToProcDefNode(function_lambda_definition functionLambdaDef)
{
procedure_definition procDef = null;
if (functionLambdaDef.return_type == null)
2018-09-18 21:45:24 +03:00
procDef = SyntaxTreeNodesConstructor.CreateProcedureDefinitionNode(new method_name(null,null, new ident(functionLambdaDef.lambda_name, functionLambdaDef.source_context), null),
2015-05-14 22:35:07 +03:00
functionLambdaDef.formal_parameters,
false,
false,
functionLambdaDef.proc_body,
functionLambdaDef.source_context);
else
procDef = SyntaxTreeNodesConstructor.CreateFunctionDefinitionNode(new method_name(null, null, new ident(functionLambdaDef.lambda_name), null),
functionLambdaDef.formal_parameters,
false,
false,
functionLambdaDef.proc_body,
functionLambdaDef.return_type,
functionLambdaDef.source_context);
procDef.proc_header.name.meth_name.source_context = procDef.proc_header.source_context;
functionLambdaDef.proc_definition = procDef;
return procDef;
}
#region Поиск всех result - ов
public class ResultNodesSearcher : SyntaxTree.WalkingVisitorNew // Нигде в проекте не используется потому что expr могут использовать локальные переменные, тип которых неизвестен до семантического разбора
2015-05-14 22:35:07 +03:00
{
public List<expression> exprList = new List<expression>();
public ResultNodesSearcher(syntax_tree_node root)
{
ProcessNode(root);
}
2019-06-30 08:41:59 +03:00
public override void visit(function_lambda_definition fd)
{
// не заходить во внутренние лямбды
}
2015-05-14 22:35:07 +03:00
public override void visit(assign value)
{
var to = value.to as ident;
if (to != null && value.operator_type == Operators.Assignment && to.name.ToLower() == "result")
{
exprList.Add(value.from);
}
}
2019-06-19 18:53:05 +03:00
/*public override void visit(function_lambda_definition fld)
{
}*/
2015-05-14 22:35:07 +03:00
}
#endregion
#region Генерация новых узлов синтаксического дерева
/// <summary>
/// Класс для генерации новых узлов синтаксического дерева
/// </summary>
public class SyntaxTreeNodesConstructor
{
public static var_statement CreateVarStatementNode(string idName, type_definition varType, expression initValue)
{
var id = new ident(idName);
var idlist = new ident_list(id);
2015-05-14 22:35:07 +03:00
var vdef = new var_def_statement(idlist, varType, initValue, definition_attribute.None, false, null);
return new var_statement(vdef, null);
}
public static var_statement CreateVarStatementNode(string idName, string varTypeName, expression initValue)
{
var id = new ident(idName);
var idlist = new ident_list(id);
2015-05-14 22:35:07 +03:00
var varType = new named_type_reference(varTypeName, null);
var vdef = new var_def_statement(idlist, varType, initValue, definition_attribute.None, false, null);
return new var_statement(vdef, null);
}
public static var_statement CreateVarStatementNode(ident_list idlist, type_definition varType, expression initValue)
{
var vdef = new var_def_statement(idlist, varType, initValue, definition_attribute.None, false, null);
return new var_statement(vdef, null);
}
public static procedure_definition CreateProcedureDefinitionNode(method_name methName, formal_parameters formalPars, bool ofObject, bool classKeyword, statement procBody, SourceContext sc)
{
procedure_definition procDef = new procedure_definition();
procedure_header procHeader = new procedure_header();
procHeader.name = methName;
procHeader.source_context = sc;
if (procHeader.name.meth_name is template_type_name)
{
procHeader.template_args = (procHeader.name.meth_name as template_type_name).template_args;
ident id = new ident(procHeader.name.meth_name.name);
procHeader.name.meth_name = id;
}
procHeader.parameters = formalPars;
procHeader.of_object = ofObject;
procHeader.class_keyword = classKeyword;
statement_list stmtList = new statement_list();
stmtList.subnodes.Add(procBody);
block bl = new block(null, null);
bl.program_code = stmtList;
procDef.proc_header = procHeader;
procDef.proc_body = (proc_block)bl;
return procDef;
}
public static procedure_definition CreateFunctionDefinitionNode(method_name methName, formal_parameters formalPars, bool ofObject, bool classKeyword, statement procBody, type_definition returnType, SourceContext sc)
{
procedure_definition procDef = new procedure_definition();
function_header procHeader = new function_header();
procHeader.name = methName;
procHeader.source_context = sc;
if (procHeader.name.meth_name is template_type_name)
{
procHeader.template_args = (procHeader.name.meth_name as template_type_name).template_args;
ident id = new ident(procHeader.name.meth_name.name);
procHeader.name.meth_name = id;
}
procHeader.parameters = formalPars;
procHeader.of_object = ofObject;
procHeader.class_keyword = classKeyword;
procHeader.return_type = returnType;
statement_list stmtList = new statement_list();
stmtList.subnodes.Add(procBody);
block bl = new block(null, null);
bl.program_code = stmtList;
procDef.proc_header = procHeader;
procDef.proc_body = (proc_block)bl;
return procDef;
}
}
#endregion
}
}