pascalabcnet/TreeConverter/LambdaExpressions/Closure/CapturedVariablesSubstitutor.cs
Александр Земляк 1f4b44c2c0
Исправление ошибок при перекомпиляции pcu файлов (#3398)
* Move yield string constants to StringConstants class

* Add GeneratedNamesManager class to store all generated names during unit compilation

* Make GeneratedNamesManager class non static

* Add Utils project to installer files

* Add GeneratedNamesManager usage in CapturedVariablesSubstitutionsManager

* Move GeneratedNamesManager initialization in CompilationUnit constructor

* More GeneratedNamesManager usage

* QuestionPointDesugarVisitor fix
2026-03-08 18:30:33 +03:00

1166 lines
58 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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<syntax_tree_node, int> 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<SubstitutionKey, List<ident>> _identsReferences;
private readonly Dictionary<int, CapturedVariablesSubstitutionClassGenerator.ScopeClassDefinition> _generatedScopeClassesInfo;
private readonly Dictionary<SubstitutionKey, dot_node> _substitutionsInfo;
private readonly syntax_tree_visitor _visitor;
private readonly Stack<syntax_tree_node> _syntaxTreeNodeStack = new Stack<syntax_tree_node>();
private readonly Dictionary<int, CapturedVariablesTreeNode> _capturedVarsTreeNodesDictionary;
private readonly List<CapturedVariablesTreeNodeLambdaScope> _lambdasToBeAddedAsMethods;
private readonly Dictionary<string, Tuple<string, class_field, semantic_node>> _mappingForNonPublicFieldsOfClass;
private readonly GeneratedNamesManager _generatedNamesManager;
public CapturedVariablesSubstitutor(Dictionary<SubstitutionKey, List<ident>> identsReferences,
Dictionary<int, CapturedVariablesSubstitutionClassGenerator.ScopeClassDefinition> generatedScopeClassesInfo,
List<CapturedVariablesTreeNodeLambdaScope> lambdasToBeAddedAsMethods,
Dictionary<SubstitutionKey, dot_node> substitutionsInfo,
Dictionary<int, CapturedVariablesTreeNode> capturedVarsTreeNodesDictionary,
Dictionary<string, Tuple<string, class_field, semantic_node>> 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<CapturedVariablesTreeNodeLambdaScope> GetNestedLambdasToBeAddedToThisClass(CapturedVariablesTreeNode scope)
{
var res = new List<CapturedVariablesTreeNodeLambdaScope>();
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<statement>();
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<statement>();
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<statement>();
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<statement> 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<statement>(statementListNode.subnodes);
var stmtListQueue = new Queue<statement>();
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<type_declaration, List<procedure_definition>> 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<procedure_definition>();
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<type_declaration, List<procedure_definition>>(typeDeclaration, procedures);
}
private List<procedure_definition> VisitClassDefinitions()
{
var procedures = new List<procedure_definition>();
_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<CapturedVariablesTreeNodeLambdaScope> 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<procedure_definition> 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<statement>();
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<CapturedVariablesTreeNodeLambdaScope>();
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<var_def_statement>();
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<procedure_definition>()
.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<var_def_statement>()
.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<ident> AllGenericParameters
{
get
{
if (!IsInGenerics)
{
return null;
}
var res = new List<ident>();
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<ident> ClassGenericParameters
{
get
{
if (!IsInGenerics)
{
return null;
}
var res = new List<ident>();
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<generic_parameter_eliminations> genericParameterEliminations, List<ident> pars)
{
var list = new List<where_definition>();
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<generic_parameter_eliminations>();
if (_visitor.context._ctn != null && _visitor.context._ctn.generic_params != null)
{
List<generic_parameter_eliminations> 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);
}
}
}