// Copyright (c) Ivan Bondarev, Stanislav Mikhalkovich (for details please see \doc\copyright.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; using System.Collections.Generic; using System.Linq; using PascalABCCompiler.SemanticTree; using PascalABCCompiler.SyntaxTree; using PascalABCCompiler.SystemLibrary; using PascalABCCompiler.TreeConverter; using PascalABCCompiler.TreeRealization; using array_const = PascalABCCompiler.SyntaxTree.array_const; using for_node = PascalABCCompiler.SyntaxTree.for_node; using PascalABCCompiler.CoreUtils; namespace TreeConverter.LambdaExpressions.Closure { internal class SubstitutionKey { public override string ToString() => _variableName; private readonly string _variableName; private readonly syntax_tree_node _syntaxTreeNodeWhereVaribleIsDeclared; private readonly syntax_tree_node _syntaxTreeUnderSubstitution; private readonly StringComparison stringComparison; public SubstitutionKey(string varName, syntax_tree_node varDefLocation, syntax_tree_node block) { _variableName = varName; _syntaxTreeNodeWhereVaribleIsDeclared = varDefLocation; _syntaxTreeUnderSubstitution = block; stringComparison = SemanticRulesConstants.SymbolTableCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; } public bool IsPartialKeyEquals(syntax_tree_node block) { return block == _syntaxTreeUnderSubstitution; } public override bool Equals(object obj) { var other = obj as SubstitutionKey; if (other == null) { return false; } return _variableName.Equals(other._variableName, stringComparison) && _syntaxTreeNodeWhereVaribleIsDeclared == other._syntaxTreeNodeWhereVaribleIsDeclared && _syntaxTreeUnderSubstitution == other._syntaxTreeUnderSubstitution; } public override int GetHashCode() { Func getHashCodeOfSyntaxTreeNode = stn => { if (stn == null || stn.source_context == null) { return 0; } var begin = stn.source_context.begin_position; var end = stn.source_context.end_position; return (begin == null ? 0 : begin.column_num.GetHashCode() ^ begin.line_num.GetHashCode()) ^ (end == null ? 0 : end.column_num.GetHashCode() ^ end.line_num.GetHashCode()); }; return (stringComparison == StringComparison.Ordinal ? _variableName.GetHashCode() : _variableName.ToLower().GetHashCode()) ^ (_syntaxTreeNodeWhereVaribleIsDeclared == null ? 0 : _syntaxTreeNodeWhereVaribleIsDeclared.GetHashCode()) ^ (_syntaxTreeUnderSubstitution == null ? 0 : _syntaxTreeUnderSubstitution.GetHashCode()); } } internal class CapturedVariablesSubstitutor : WalkingVisitorNew { private readonly Dictionary> _identsReferences; private readonly Dictionary _generatedScopeClassesInfo; private readonly Dictionary _substitutionsInfo; private readonly syntax_tree_visitor _visitor; private readonly Stack _syntaxTreeNodeStack = new Stack(); private readonly Dictionary _capturedVarsTreeNodesDictionary; private readonly List _lambdasToBeAddedAsMethods; private readonly Dictionary> _mappingForNonPublicFieldsOfClass; private readonly GeneratedNamesManager _generatedNamesManager; public CapturedVariablesSubstitutor(Dictionary> identsReferences, Dictionary generatedScopeClassesInfo, List lambdasToBeAddedAsMethods, Dictionary substitutionsInfo, Dictionary capturedVarsTreeNodesDictionary, Dictionary> mappingForNonPublicFieldsOfClass, syntax_tree_visitor visitor, GeneratedNamesManager generatedNamesManager) { _identsReferences = identsReferences; _generatedScopeClassesInfo = generatedScopeClassesInfo; _substitutionsInfo = substitutionsInfo; _capturedVarsTreeNodesDictionary = capturedVarsTreeNodesDictionary; _lambdasToBeAddedAsMethods = lambdasToBeAddedAsMethods; _mappingForNonPublicFieldsOfClass = mappingForNonPublicFieldsOfClass; _visitor = visitor; _generatedNamesManager = generatedNamesManager; } public override void visit(function_lambda_definition fld) { // Пробуем не обходить proc_definition - сделали её object в NodesGenerator DefaultVisit(fld); // Это решило проблему с вложенными лямбдами без var a := lambda } public override void visit(statement_list stmtList) { /*#if DEBUG var s = stmtList.subnodes.Count + " " + stmtList.subnodes[0].ToString() + "\n"; System.IO.File.AppendAllText("d:\\bb3.txt", s); #endif*/ if (!stmtList.IsInternal) _syntaxTreeNodeStack.Push(stmtList); base.visit(stmtList); if (!stmtList.IsInternal) _syntaxTreeNodeStack.Pop(); } public override void visit(for_node fn) { SubstituteInNode(fn, fn); //Здесь обработаются замены поверхностного уровня, то есть, если в initial_value и т.д. содержатся ident _syntaxTreeNodeStack.Push(fn); //Здесь обработаются случаи, если initial_value и т.д. представляют собой более сложные случаи, такие как indexer и т.д. ProcessNode(fn.initial_value); ProcessNode(fn.finish_value); ProcessNode(fn.increment_value); _syntaxTreeNodeStack.Pop(); ProcessNode(fn.statements); } public override void visit(foreach_stmt fn) { SubstituteInNode(fn, fn); //Здесь обработаются замены поверхностного уровня, то есть, если в initial_value и т.д. содержатся ident _syntaxTreeNodeStack.Push(fn); //Здесь обработаются случаи, если in_what представляет собой более сложные случаи, такие как indexer и т.д. ProcessNode(fn.in_what); _syntaxTreeNodeStack.Pop(); ProcessNode(fn.stmt); } private void AddPropertiesToConvertingClass() { if (_visitor.context._ctn != null) { if (_mappingForNonPublicFieldsOfClass != null) { var pairs = _mappingForNonPublicFieldsOfClass.Where(pair => pair.Value.Item3 is common_property_node); foreach (var pair in pairs) { var name = pair.Value.Item1; var semClassField = pair.Value.Item2; var pn = (common_property_node)pair.Value.Item3; var readFunction = _visitor.GenerateGetMethodForField(pn, PascalABCCompiler.StringConstants.GetGetAccessorName(pn.name), semClassField, null); var writeFunction = _visitor.GenerateSetMethodForField(pn, PascalABCCompiler.StringConstants.GetSetAccessorName(pn.name), semClassField, null); pn.internal_get_function = readFunction; pn.internal_set_function = writeFunction; _visitor.context._ctn.Scope.AddSymbol(name, new SymbolInfo(pn)); _visitor.context._ctn.properties.AddElement(pn); } } } } private void SubstituteInNode(syntax_tree_node currentNode, syntax_tree_node nodeWhereSubstitute) { var identSubnodesIndexes = Enumerable .Range(0, nodeWhereSubstitute.subnodes_count) .Where(index => nodeWhereSubstitute[index] is ident) .ToList(); if (nodeWhereSubstitute is for_node) //нужно исключить переменную цикла, так как она оброабатывается отдельно в SubstituteForLoopVariables { var fn = (for_node) nodeWhereSubstitute; var indexWithLoopIdent = identSubnodesIndexes.First(ind => { var id = nodeWhereSubstitute[ind] as ident; if (id == null) { return false; } return id == fn.loop_variable; }); identSubnodesIndexes.Remove(indexWithLoopIdent); } if (nodeWhereSubstitute is foreach_stmt) //нужно исключить переменную цикла, так как она оброабатывается отдельно в SubstituteForEachLoopVariables { var fn = (foreach_stmt)nodeWhereSubstitute; var indexWithLoopIdent = identSubnodesIndexes.First(ind => { var id = nodeWhereSubstitute[ind] as ident; if (id == null) { return false; } return id == fn.identifier; }); identSubnodesIndexes.Remove(indexWithLoopIdent); } if (identSubnodesIndexes.Count == 0) { return; } var keys = _substitutionsInfo .Keys .Where(k => k.IsPartialKeyEquals(currentNode)) .Where(k => _identsReferences.ContainsKey(k)); foreach (var key in keys) { var subst = _substitutionsInfo[key]; foreach (var id in _identsReferences[key]) { for (var i = 0; i < identSubnodesIndexes.Count; i++) { if (nodeWhereSubstitute[identSubnodesIndexes[i]] == id) { closure_substituting_node csn = new closure_substituting_node(subst); if (subst.right is ident) { csn.name = (subst.right as ident).name; //if (id.name.ToLower() == "result") // (subst.right as ident).source_context = id.source_context; csn.source_context = id.source_context; } nodeWhereSubstitute[identSubnodesIndexes[i]] = csn; } } } } } private void OnNodeLeave(syntax_tree_node node) { if (_syntaxTreeNodeStack.Count == 0) { return; } if (node is for_node || node is foreach_stmt) // заголовки циклов обрабатываются отдельно { return; } var currentBlock = _syntaxTreeNodeStack.Peek(); SubstituteInNode(currentBlock, node); } private List GetNestedLambdasToBeAddedToThisClass(CapturedVariablesTreeNode scope) { var res = new List(); foreach (var child in scope.ChildNodes) { var childAsLambda = child as CapturedVariablesTreeNodeLambdaScope; if (childAsLambda == null) { res.AddRange(GetNestedLambdasToBeAddedToThisClass(child)); } else { if (childAsLambda.ScopeIndexOfClassWhereLambdaWillBeAddedAsMethod.HasValue) { continue; } else { res.Add(childAsLambda); res.AddRange(GetNestedLambdasToBeAddedToThisClass(childAsLambda)); } } } return res; } private void SubstituteLambdas() { for (var i = 0; i < _lambdasToBeAddedAsMethods.Count; i++) { if (_lambdasToBeAddedAsMethods[i].ScopeIndexOfClassWhereLambdaWillBeAddedAsMethod != null) { var classDef = _generatedScopeClassesInfo[_lambdasToBeAddedAsMethods[i].ScopeIndexOfClassWhereLambdaWillBeAddedAsMethod.Value]; if (classDef.CorrespondingTreeNode is CapturedVariablesTreeNodeClassScope) { continue; } var otherLambdasToBeAddedAsMethods = GetNestedLambdasToBeAddedToThisClass(_lambdasToBeAddedAsMethods[i]); classDef.NestedLambdas.AddRange(otherLambdasToBeAddedAsMethods); foreach (var oltaam in otherLambdasToBeAddedAsMethods) { oltaam.LambdaDefinition.substituting_node = new ident(oltaam.LambdaDefinition.lambda_name); } var left = new ident(classDef.GeneratedSubstitutingFieldName); var lambdaDef = _lambdasToBeAddedAsMethods[i].LambdaDefinition; lambdaDef.substituting_node = new dot_node(left, new ident(lambdaDef.lambda_name)); /*var classDecl = (class_definition)classDef.ClassDeclaration.type_def; var procDecl = LambdaHelper.ConvertLambdaNodeToProcDefNode(lambdaDef); classDecl.body.Add(SyntaxTreeBuilder.BuildOneMemberSection(procDecl));*/ } } } private void SubstituteForLoopVariables() { var forScopes = _capturedVarsTreeNodesDictionary.Where(n => n.Value is CapturedVariablesTreeNodeForScope) .Select(n => (CapturedVariablesTreeNodeForScope) n.Value) .ToList(); foreach (var forScope in forScopes) { var forNode = forScope.CorrespondingSyntaxTreeNode as for_node; if (forNode != null) { CapturedVariablesSubstitutionClassGenerator.ScopeClassDefinition generatedClass; if (_generatedScopeClassesInfo.TryGetValue(forScope.ScopeIndex, out generatedClass)) { var nodesToAdd = new List(); var enclosedStatementList = (statement_list) forScope.EnclosedUpperBlockScope.CorrespondingSyntaxTreeNode; nodesToAdd.Add(generatedClass.GeneratedVarStatementForScope); if (generatedClass.AssignNodeForUpperClassFieldInitialization != null) { nodesToAdd.Add(generatedClass.AssignNodeForUpperClassFieldInitialization); } var forNodeIndex = enclosedStatementList.subnodes.FindIndex(stmt => stmt == forNode); if (forNodeIndex == -1) // SSM 30.10.18 грубый fix #1443 Если for_node находится на 2 и более уровней ниже, то мы её нне найдём - пробуем вставить в начало блока forNodeIndex = 0; // Конечно, могут возникнуть проблемы что мы вставляем в начало блока, но если там нет перекрывающихся имен, то вроде и проблем нет enclosedStatementList.subnodes.InsertRange(forNodeIndex, nodesToAdd); var substDotNode = new dot_node(new ident(generatedClass.GeneratedSubstitutingFieldName), new ident(forNode.loop_variable.name)); var assignNode = new assign(substDotNode, new ident(forNode.loop_variable.name)); // TODO: SourceContexts!!!!! ((statement_list)forNode.statements).subnodes.Insert(0, assignNode); } else { var si = forScope.SymbolInfoLoopVar; if (si.scope == null || !_capturedVarsTreeNodesDictionary.ContainsKey(si.scope.ScopeNum)) return; var scopeWhereVarDefined = _capturedVarsTreeNodesDictionary[si.scope.ScopeNum]; var idRef = scopeWhereVarDefined .VariablesDefinedInScope .Find(var => var.SymbolInfo == si); //TODO: !!!!!!!!!!!!!!!!!!!!!! var substKey = new SubstitutionKey(forNode.loop_variable.name, idRef.SyntaxTreeNodeWithVarDeclaration, forNode); if (_substitutionsInfo.ContainsKey(substKey)) // Если нашли замену, то переменная где-то захватывалась { forNode.create_loop_variable = true; var substDotNode = _substitutionsInfo[substKey]; var assignNode = new assign(substDotNode, new ident(forNode.loop_variable.name)); // TODO: SourceContexts!!!!! (((statement_list)forNode.statements).subnodes).Insert(0, assignNode); } } } } } private void SubstituteForEachLoopVariables() { var forEachScopes = _capturedVarsTreeNodesDictionary.Where(n => n.Value is CapturedVariablesTreeNodeForEachScope) .Select(n => (CapturedVariablesTreeNodeForEachScope)n.Value) .ToList(); foreach (var forEachScope in forEachScopes) { var forEachNode = forEachScope.CorrespondingSyntaxTreeNode as foreach_stmt; if (forEachNode != null) { CapturedVariablesSubstitutionClassGenerator.ScopeClassDefinition generatedClass; if (_generatedScopeClassesInfo.TryGetValue(forEachScope.ScopeIndex, out generatedClass)) { var nodesToAdd = new List(); var bodyStmtList = (statement_list)forEachNode.stmt; nodesToAdd.Add(generatedClass.GeneratedVarStatementForScope); if (generatedClass.AssignNodeForUpperClassFieldInitialization != null) { nodesToAdd.Add(generatedClass.AssignNodeForUpperClassFieldInitialization); } var substDotNode = new dot_node(new ident(generatedClass.GeneratedSubstitutingFieldName), new ident(forEachNode.identifier.name)); var assignNode = new assign(substDotNode, new ident(forEachNode.identifier.name)); // TODO: SourceContexts!!!!! nodesToAdd.Add(assignNode); bodyStmtList.subnodes.InsertRange(0, nodesToAdd); } else { var si = forEachScope.SymbolInfoLoopVar; if (si.scope == null) return; var scopeWhereVarDefined = _capturedVarsTreeNodesDictionary[si.scope.ScopeNum]; var idRef = scopeWhereVarDefined .VariablesDefinedInScope .Find(var => var.SymbolInfo == si); //TODO: !!!!!!!!!!!!!!!!!!!!!! var substKey = new SubstitutionKey(forEachNode.identifier.name, idRef.SyntaxTreeNodeWithVarDeclaration, forEachNode); if (_substitutionsInfo.ContainsKey(substKey)) // Если нашли замену, то переменная где-то захватывалась { if (forEachNode.type_name == null) //Нужно, чтобы переменная цикла создалась в этом случае в контескте с этим же именем что и была { forEachNode.type_name = new no_type_foreach(); } var substDotNode = _substitutionsInfo[substKey]; var assignNode = new assign(substDotNode, new ident(forEachNode.identifier.name)); // TODO: SourceContexts!!!!! (((statement_list)forEachNode.stmt).subnodes).Insert(0, assignNode); } } } } } private void SubstituteLambdaParameters() { var lambdaScopes = _capturedVarsTreeNodesDictionary.Where(n => n.Value is CapturedVariablesTreeNodeLambdaScope) .Select(n => (CapturedVariablesTreeNodeLambdaScope)n.Value) .ToList(); foreach (var lambdaScope in lambdaScopes) { CapturedVariablesSubstitutionClassGenerator.ScopeClassDefinition generatedClass; if (_generatedScopeClassesInfo.TryGetValue(lambdaScope.ScopeIndex, out generatedClass)) { var nodesToAdd = new List(); nodesToAdd.Add(generatedClass.GeneratedVarStatementForScope); if (generatedClass.AssignNodeForUpperClassFieldInitialization != null) { nodesToAdd.Add(generatedClass.AssignNodeForUpperClassFieldInitialization); } var variables = lambdaScope .VariablesDefinedInScope .Where(l => l.ReferencingLambdas.Count > 0) .Select(var => ((IVAriableDefinitionNode) var.SymbolInfo.sym_info).name.ToLower()) .ToList(); foreach (var variable in variables) { var assignStmt = new assign(new dot_node(new ident(generatedClass.GeneratedSubstitutingFieldName), new ident(variable)), new ident(variable)); nodesToAdd.Add(assignStmt); } ((statement_list)lambdaScope.LambdaDefinition.proc_body).subnodes.InsertRange(0, nodesToAdd); } } } private void TreeStatementListToQueue(statement_list sl, Queue q) { foreach (var st in sl.list) { if (st is statement_list stl && stl.IsInternal) TreeStatementListToQueue(stl, q); else q.Enqueue(st); } } private void TreeStatementListToStatementList(statement_list sl, statement_list newsl) { newsl.left_logical_bracket = sl.left_logical_bracket; newsl.right_logical_bracket = sl.right_logical_bracket; newsl.expr_lambda_body = sl.expr_lambda_body; foreach (var st in sl.list) { if (st is statement_list stl) { TreeStatementListToStatementList(stl, newsl); } else { newsl.Add(st); st.Parent = newsl; } } } private void SubstituteVariablesDeclarations() { var classDefsTreeNodes = _generatedScopeClassesInfo.Join(_capturedVarsTreeNodesDictionary, outer => outer.Key, inner => inner.Key, (outer, inner) => new { ClassDeclaration = outer.Value, TreeNode = inner.Value }) .Where(p => p.TreeNode is CapturedVariablesTreeNodeBlockScope) .Select(p => new { p.ClassDeclaration, TreeNode = (CapturedVariablesTreeNodeBlockScope) p.TreeNode }); //TODO: Сейчас рассматриваются только захватываемые переменные, определенные внутри какого-либо блока. Рассмотреть остальные случаи. foreach (var classDefTreeNode in classDefsTreeNodes) { var statementListNode = classDefTreeNode.TreeNode.CorrespondingSyntaxTreeNode as statement_list; //TODO: Сейчас рассматриваются только захватываемые переменные, определенные внутри какого-либо блока. Рассмотреть остальные случаи. if (statementListNode != null) { var variables = classDefTreeNode .TreeNode .VariablesDefinedInScope .Where(var => var.ReferencingLambdas.Count > 0) .GroupBy(var => var.SyntaxTreeNodeWithVarDeclaration) .Select(gr => new { SyntaxTreeNodeWithVarDeclaration = gr.Key, Vars = gr.ToList() }) .ToList(); if (variables.Count == 0) { return; } var newStmtList = new statement_list(); //newStmtList.IsInternal = statementListNode.IsInternal; // Попробуем вытянуть в линию тех, у кого IsInternal=true newStmtList.Add(classDefTreeNode.ClassDeclaration.GeneratedVarStatementForScope); if (classDefTreeNode.ClassDeclaration.AssignNodeForUpperClassFieldInitialization != null) { newStmtList.Add(classDefTreeNode.ClassDeclaration.AssignNodeForUpperClassFieldInitialization); } //var stmtListQueue = new Queue(statementListNode.subnodes); var stmtListQueue = new Queue(); TreeStatementListToQueue(statementListNode, stmtListQueue); // сделали плоскую последовательность while (stmtListQueue.Count > 0) { var currentStatement = stmtListQueue.Dequeue(); var varStatement = currentStatement as var_statement; //TODO: пока что только локальные переменные, определенные внутри var_def_statement!!!!!!!! if (varStatement != null && varStatement.var_def != null) { var varDefKey = variables.FirstOrDefault(gr => gr.SyntaxTreeNodeWithVarDeclaration == varStatement.var_def); if (varDefKey != null) { var varsToExclude = varDefKey.Vars.Select(var => ((IVAriableDefinitionNode) var.SymbolInfo.sym_info).name); //TODO: пока что только локальные переменные!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! var newVarList = varStatement.var_def.vars.idents .Where(id => !varsToExclude.Contains(id.name, _visitor.context.CurrentScope.CaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase)) .ToList(); if (newVarList.Count > 0) { var newVarDefStmt = new var_def_statement(new ident_list(newVarList), varStatement.var_def.vars_type) { inital_value = varStatement.var_def.inital_value }; var newVarStmt = new var_statement(newVarDefStmt); newStmtList.Add(newVarStmt); } if (varStatement.var_def.inital_value != null) { var initVal = varStatement.var_def.inital_value; string auxVarName = null; if (initVal is array_const) { auxVarName = LambdaHelper.GetAuxVarName(_generatedNamesManager); var newVarDefStmt = new var_def_statement(new ident_list(new ident(auxVarName)), varStatement.var_def.vars_type) { inital_value = initVal }; var newVarStmt = new var_statement(newVarDefStmt); newStmtList.Add(newVarStmt); } foreach (var variable in varsToExclude) { var assignStmt = new assign(new dot_node(new ident(classDefTreeNode.ClassDeclaration.GeneratedSubstitutingFieldName), new ident(variable)), auxVarName == null ? varStatement.var_def.inital_value : new ident(auxVarName)); newStmtList.Add(assignStmt); } } } else { newStmtList.Add(currentStatement); } } else { newStmtList.Add(currentStatement); } } statementListNode.subnodes.Clear(); statementListNode.subnodes.AddRange(newStmtList.subnodes); } } } private Tuple> CreateTypeDeclarationWithForwardDeclaration(type_declaration cl) { var oldClDef = (class_definition) cl.type_def; var classDef = SyntaxTreeBuilder.BuildClassDefinition(); var typeDeclaration = new type_declaration(cl.type_name, classDef); classDef.where_section = oldClDef.where_section; var procedures = new List(); var classMembers = new class_members(access_modifer.public_modifer); classDef.body.class_def_blocks.Add(classMembers); foreach (var member in oldClDef.body.class_def_blocks.SelectMany(x => x.members)) { if (member is var_def_statement) { classMembers.Add(member); } else { var procDef = (procedure_definition) member; if (procDef.proc_header is constructor) { classMembers.Add(procDef); continue; } procedure_header procHeader; if (procDef.proc_header is function_header) { var fh = (function_header) procDef.proc_header; procHeader = new function_header { name = new method_name(fh.name.meth_name.name), source_context = fh.source_context, parameters = fh.parameters, of_object = fh.of_object, class_keyword = fh.class_keyword }; ((function_header)procHeader).return_type = fh.return_type; } else { procHeader = new procedure_header { name = new method_name(procDef.proc_header.name.meth_name.name), source_context = procDef.proc_header.source_context, parameters = procDef.proc_header.parameters, of_object = procDef.proc_header.of_object, class_keyword = procDef.proc_header.class_keyword }; } procDef.proc_header.name.class_name = cl.type_name; procedures.Add(procDef); classMembers.Add(procHeader); } } return new Tuple>(typeDeclaration, procedures); } private List VisitClassDefinitions() { var procedures = new List(); _visitor.context.SaveContextAndUpToGlobalLevel(); try { _visitor.lambdaProcessingState = LambdaProcessingState.ClosuresProcessingVisitGeneratedClassesPhase; var lambdaGroups = _lambdasToBeAddedAsMethods.GroupBy(l => l.ScopeIndexOfClassWhereLambdaWillBeAddedAsMethod) .ToDictionary(x => x.Key, x => x.ToList()); foreach (var clDecl in _generatedScopeClassesInfo.Where(cd => !(cd.Value.CorrespondingTreeNode is CapturedVariablesTreeNodeClassScope)).OrderBy(cd => cd.Key)) { var lambdas = clDecl.Value.NestedLambdas; List ls; if (lambdaGroups.TryGetValue(clDecl.Key, out ls)) { lambdas = lambdas.Concat(ls).ToList(); } var classDecl = (class_definition)clDecl.Value.ClassDeclaration.type_def; foreach (var lambdaDef in lambdas) { var procDecl = LambdaHelper.ConvertLambdaNodeToProcDefNode(lambdaDef.LambdaDefinition); classDecl.body.Add(SyntaxTreeBuilder.BuildOneMemberSection(procDecl)); } var cl = CreateTypeDeclarationWithForwardDeclaration(clDecl.Value.ClassDeclaration); procedures.AddRange(cl.Item2); _visitor.visit(cl.Item1); } } finally { _visitor.context.RestoreCurrentContext(); _visitor.lambdaProcessingState = LambdaProcessingState.ClosuresProcessingPhase; } return procedures; } private void VisitProceduresOfClasses(List procedures) { _visitor.context.SaveContextAndUpToGlobalLevel(); _visitor.lambdaProcessingState = LambdaProcessingState.ClosuresProcessingVisitGeneratedClassesPhase; try { procedures.ForEach(p => _visitor.visit(p)); } finally { _visitor.context.RestoreCurrentContext(); _visitor.lambdaProcessingState = LambdaProcessingState.ClosuresProcessingPhase; } } private void SubstituteVarDefInProcedure(statement_list statementList) //TODO: сейчас обрабатывается так, как будто блока объявлений declarations в функции нет. Нужно будет предусмотреть, но не только для функций, а еще для всех узлов где есть block, который содержит declarations { var classDefsTreeNode = _generatedScopeClassesInfo.Join(_capturedVarsTreeNodesDictionary, outer => outer.Key, inner => inner.Key, (outer, inner) => new { ClassDeclaration = outer.Value, TreeNode = inner.Value }) .Where(p => p.TreeNode is CapturedVariablesTreeNodeProcedureScope) .Select(p => new { p.ClassDeclaration, TreeNode = (CapturedVariablesTreeNodeProcedureScope)p.TreeNode }) .FirstOrDefault(); if (classDefsTreeNode == null) { return; } var variables = classDefsTreeNode .TreeNode .VariablesDefinedInScope .Where(l => l.ReferencingLambdas.Count > 0) .ToList(); if (variables.Count == 0) { return; } CapturedVariablesSubstitutionClassGenerator.ScopeClassDefinition generatedClass; if (_generatedScopeClassesInfo.TryGetValue(classDefsTreeNode.TreeNode.ScopeIndex, out generatedClass)) { var exprList = variables.Select(v => (expression)new ident(((IVAriableDefinitionNode) v.SymbolInfo.sym_info).name, v.SymbolInfo.sym_info.location)).ToList(); if (IsInGenerics) { var newVarDefSyntaxTreeNode = new var_def_statement(generatedClass.GeneratedSubstitutingFieldName, new template_type_reference(new named_type_reference(generatedClass.ClassDeclaration.type_name.name), new template_param_list(AllGenericParameters.Select(l => SyntaxTreeBuilder.BuildSimpleType(l.name)).ToList()))); newVarDefSyntaxTreeNode.inital_value = new new_expr(new template_type_reference(new named_type_reference(generatedClass.ClassDeclaration.type_name.name), new template_param_list(AllGenericParameters.Select(l => SyntaxTreeBuilder.BuildSimpleType(l.name)).ToList())), new expression_list(exprList), null); generatedClass.GeneratedVarStatementForScope.var_def = newVarDefSyntaxTreeNode; } else { generatedClass.GeneratedVarStatementForScope.var_def.inital_value = new new_expr(SyntaxTreeBuilder.BuildSimpleType(generatedClass.ClassDeclaration.type_name.name), new expression_list(exprList), null); } var nodesToAdd = new List(); nodesToAdd.Add(generatedClass.GeneratedVarStatementForScope); if (generatedClass.AssignNodeForUpperClassFieldInitialization != null) { nodesToAdd.Add(generatedClass.AssignNodeForUpperClassFieldInitialization); } int ind = 0; if (statementList.subnodes.Count > 0 && statementList.subnodes[0] is procedure_call && (statementList.subnodes[0] as procedure_call).func_name is inherited_ident) ind = 1; statementList.subnodes.InsertRange(ind, nodesToAdd); } } private void SubstituteTypesInCaseOfGenerics() { if (!IsInGenerics) { return; } /* TODO: нужно заменить в полях сгенерированных классов, параметрах и возвращаемом значении лямбд - у всех, с учетом того как чистятся все дженерики... /* Предусмотреть случай, когда захвата переменных не происходит, но мы все равно находимся в лямбде - это нужно сделать на FinishPhase в syntax_tree_visitor /* Не забыть про where секцию */ var nestedLambdas = new List(); foreach (var cl in _generatedScopeClassesInfo.Where(cd => !(cd.Value.CorrespondingTreeNode is CapturedVariablesTreeNodeClassScope))) { nestedLambdas.AddRange(cl.Value.NestedLambdas); } foreach (CapturedVariablesTreeNodeLambdaScope t in _lambdasToBeAddedAsMethods.Concat(nestedLambdas)) { var lambdaDef = t.LambdaDefinition; if (lambdaDef.formal_parameters != null && lambdaDef.formal_parameters.params_list.Count > 0) { for (var j = 0; j < lambdaDef.formal_parameters.params_list.Count; j++) { if (lambdaDef.formal_parameters.params_list[j].vars_type is lambda_inferred_type) { if ((lambdaDef.formal_parameters.params_list[j].vars_type as lambda_inferred_type).real_type is type_node) { lambdaDef.formal_parameters.params_list[j].vars_type = LambdaHelper.ConvertSemanticTypeToSyntaxType((type_node)(lambdaDef.formal_parameters.params_list[j].vars_type as lambda_inferred_type).real_type); } } } } if (lambdaDef.return_type is lambda_inferred_type) { if ((lambdaDef.return_type as lambda_inferred_type).real_type is type_node) { lambdaDef.return_type = LambdaHelper.ConvertSemanticTypeToSyntaxType((type_node)(lambdaDef.return_type as lambda_inferred_type).real_type); } } } foreach (var clDecl in _generatedScopeClassesInfo.Where(cd => !(cd.Value.CorrespondingTreeNode is CapturedVariablesTreeNodeClassScope))) { var fields = ((class_definition) clDecl.Value.ClassDeclaration.type_def).body.class_def_blocks .SelectMany(d => d.members) .OfType(); foreach (var field in fields) { var semTypeNode = field.vars_type as semantic_type_node; if (semTypeNode != null) { if (semTypeNode.type is type_node) { field.vars_type = LambdaHelper.ConvertSemanticTypeToSyntaxType((type_node) semTypeNode.type); } } } var constructors = ((class_definition)clDecl.Value.ClassDeclaration.type_def).body.class_def_blocks .SelectMany(d => d.members) .OfType() .Where(pr => pr.proc_header is constructor); foreach (var constructor in constructors) { if (constructor.proc_header != null && constructor.proc_header.parameters != null && constructor.proc_header.parameters.params_list != null) { var pars = constructor.proc_header.parameters.params_list; foreach (var p in pars) { var semTypeNode = p.vars_type as semantic_type_node; if (semTypeNode != null) { if (semTypeNode.type is type_node) { p.vars_type = LambdaHelper.ConvertSemanticTypeToSyntaxType((type_node)semTypeNode.type); } } } } } var upperField = ((class_definition)clDecl.Value.ClassDeclaration.type_def).body.class_def_blocks .SelectMany(d => d.members) .OfType() .FirstOrDefault(vds => { if (vds.vars != null && vds.vars.idents != null) { if (vds.vars.idents.FirstOrDefault(id => id.name == clDecl.Value.GeneratedUpperClassFieldName) != null) { return true; } } return false; }); if (upperField != null) { var genericParameters = AllGenericParameters; if (_visitor.context._ctn != null /* && _visitor.context._ctn.generic_params != null */) { var tr = upperField.vars_type as named_type_reference; if (tr != null && tr.names != null && tr.names.Count == 1) { if (tr.names[0].name == _visitor.context._ctn.name) { genericParameters = ClassGenericParameters; } } } if (genericParameters.Count > 0) upperField.vars_type = // SSM 26/06/19 - было вне if - поставил в if - #1947 - что-то легло. Оставил вне if new template_type_reference( (named_type_reference)upperField.vars_type, new template_param_list(genericParameters.Select(l => SyntaxTreeBuilder.BuildSimpleType(l.name)).ToList())); } if (clDecl.Value.GeneratedVarStatementForScope != null) { var oldInitialValueExpressions = ((new_expr) clDecl.Value.GeneratedVarStatementForScope.var_def.inital_value).params_list; var newVarDefSyntaxTreeNode = new var_def_statement(clDecl.Value.GeneratedSubstitutingFieldName, new template_type_reference(new named_type_reference(clDecl.Value.ClassDeclaration.type_name.name), new template_param_list(AllGenericParameters.Select(l => SyntaxTreeBuilder.BuildSimpleType(l.name)).ToList()))); newVarDefSyntaxTreeNode.inital_value = new new_expr(new template_type_reference(new named_type_reference(clDecl.Value.ClassDeclaration.type_name.name), new template_param_list(AllGenericParameters.Select(l => SyntaxTreeBuilder.BuildSimpleType(l.name)).ToList())), oldInitialValueExpressions, null); clDecl.Value.GeneratedVarStatementForScope.var_def = newVarDefSyntaxTreeNode; } clDecl.Value.ClassDeclaration.type_name = new template_type_name(clDecl.Value.ClassDeclaration.type_name.name, new ident_list(AllGenericParameters)); if (WhereSection != null && WhereSection.defs != null && WhereSection.defs.Count != 0) { ((class_definition) clDecl.Value.ClassDeclaration.type_def).where_section = WhereSection; } } } private bool IsInGenerics { get { var funcStackAsList = _visitor.context.func_stack == null ? null : _visitor.context.func_stack.CloneInternalStack().ToList(); var funcIsGeneric = false; if (funcStackAsList != null) { funcIsGeneric = funcStackAsList.FirstOrDefault(f => f.generic_params != null) != null; } return funcIsGeneric || _visitor.context._ctn != null && _visitor.context._ctn.generic_params != null; } } private bool IsInGenericClass { get { return _visitor.context._ctn != null && _visitor.context._ctn.generic_params != null; } } private List AllGenericParameters { get { if (!IsInGenerics) { return null; } var res = new List(); if (_visitor.context._ctn != null && _visitor.context._ctn.generic_params != null) { res.AddRange(_visitor.context._ctn.generic_params.Select(par => new ident(par.name))); } var funcStackAsList = _visitor.context.func_stack == null ? null : _visitor.context.func_stack.CloneInternalStack().ToList(); if (funcStackAsList != null) { var funcGeneric = funcStackAsList.FirstOrDefault(f => f.generic_params != null); if (funcGeneric != null) { res.AddRange(funcGeneric.generic_params.Select(par => new ident(par.name))); } } return res; } } private List ClassGenericParameters { get { if (!IsInGenerics) { return null; } var res = new List(); if (_visitor.context._ctn != null && _visitor.context._ctn.generic_params != null) { res.AddRange(_visitor.context._ctn.generic_params.Select(par => new ident(par.name))); } return res; } } public static where_definition_list GetWhereSection(List genericParameterEliminations, List pars) { var list = new List(); for (var i = 0; i < pars.Count; i++) { var el = genericParameterEliminations[i]; var par = pars[i]; var whereDef = new where_definition { names = new ident_list(par), types = new where_type_specificator_list() }; var base_describes_type = false; if (el.base_class != SystemLibrary.object_type) { base_describes_type = el.base_class != SystemLibrary.enum_base_type; var d = LambdaHelper.ConvertSemanticTypeToSyntaxType(el.base_class); whereDef.types.Add(d); } if (el.is_class && !base_describes_type) { var d = new declaration_specificator(DeclarationSpecificator.WhereDefClass, "class"); whereDef.types.Add(d); } if (el.is_value) { if (base_describes_type) throw new InvalidOperationException($"where with record and base other then enum"); var d = new declaration_specificator(DeclarationSpecificator.WhereDefValueType, "record"); whereDef.types.Add(d); } foreach (var interf in el.implementing_interfaces) { var d = LambdaHelper.ConvertSemanticTypeToSyntaxType(interf); whereDef.types.Add(d); } if (el.has_explicit_default_ctor) { var d = new declaration_specificator(DeclarationSpecificator.WhereDefConstructor, "constructor"); whereDef.types.Add(d); } if (whereDef.types.defs.Count != 0) { list.Add(whereDef); } } return new where_definition_list(list); } private where_definition_list _whereSection = null; private where_definition_list WhereSection { get { if (!IsInGenerics) { return null; } if (_whereSection != null) { return _whereSection; } var genericParameterEliminations = new List(); if (_visitor.context._ctn != null && _visitor.context._ctn.generic_params != null) { List lst = generic_parameter_eliminations.make_eliminations_common(_visitor.context._ctn.generic_params); for (int i = 0; i < lst.Count; i++) if (!lst[i].has_default_ctor && _visitor.context._ctn.generic_params[i] is common_type_node) { lst[i].has_default_ctor = (_visitor.context._ctn.generic_params[i] as common_type_node).has_default_constructor; lst[i].has_explicit_default_ctor = (_visitor.context._ctn.generic_params[i] as common_type_node).has_explicit_default_constructor; } genericParameterEliminations.AddRange(lst); } if (_visitor.context.top_function != null && _visitor.context.top_function.generic_params != null) { genericParameterEliminations.AddRange(generic_parameter_eliminations.make_eliminations_common(_visitor.context.top_function.generic_params)); } _whereSection = GetWhereSection(genericParameterEliminations, AllGenericParameters); return _whereSection; } } public void Substitute(statement_list statementList) { OnLeave += OnNodeLeave; ProcessNode(statementList); SubstituteLambdas(); SubstituteTypesInCaseOfGenerics(); AddPropertiesToConvertingClass(); var procedures = VisitClassDefinitions(); SubstituteVariablesDeclarations(); SubstituteForLoopVariables(); SubstituteForEachLoopVariables(); SubstituteVarDefInProcedure(statementList); //SubstituteLambdaParameters(); VisitProceduresOfClasses(procedures); } } }