diff --git a/Configuration/GlobalAssemblyInfo.cs b/Configuration/GlobalAssemblyInfo.cs index 32b5ffe53..bbcb6a0fd 100644 --- a/Configuration/GlobalAssemblyInfo.cs +++ b/Configuration/GlobalAssemblyInfo.cs @@ -15,7 +15,7 @@ internal static class RevisionClass public const string Major = "3"; public const string Minor = "5"; public const string Build = "0"; - public const string Revision = "2157"; + public const string Revision = "2165"; public const string MainVersion = Major + "." + Minor; public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision; diff --git a/Configuration/Version.defs b/Configuration/Version.defs index 1294e9e0e..398f7125a 100644 --- a/Configuration/Version.defs +++ b/Configuration/Version.defs @@ -1,4 +1,4 @@ %MINOR%=5 -%REVISION%=2157 +%REVISION%=2165 %COREVERSION%=0 %MAJOR%=3 diff --git a/ReleaseGenerators/PascalABCNET_version.nsh b/ReleaseGenerators/PascalABCNET_version.nsh index b48c6d068..f816349fa 100644 --- a/ReleaseGenerators/PascalABCNET_version.nsh +++ b/ReleaseGenerators/PascalABCNET_version.nsh @@ -1 +1 @@ -!define VERSION '3.5.0.2157' +!define VERSION '3.5.0.2165' diff --git a/SyntaxTreeConverters/StandardSyntaxConverter.cs b/SyntaxTreeConverters/StandardSyntaxConverter.cs index 455b0adbc..494f68eea 100644 --- a/SyntaxTreeConverters/StandardSyntaxConverter.cs +++ b/SyntaxTreeConverters/StandardSyntaxConverter.cs @@ -22,7 +22,7 @@ namespace PascalABCCompiler.SyntaxTreeConverters // Выносим выражения с лямбдами из заголовка foreach StandOutExprWithLambdaInForeachSequenceVisitor.New.ProcessNode(root); VarNamesInMethodsWithSameNameAsClassGenericParamsReplacer.New.ProcessNode(root); // SSM bug fix #1147 - + FindOnExceptVarsAndApplyRenameVisitor.New.ProcessNode(root); #if DEBUG //new SimplePrettyPrinterVisitor("E:/projs/out.txt").ProcessNode(root); #endif diff --git a/SyntaxVisitors/SyntaxVisitors.csproj b/SyntaxVisitors/SyntaxVisitors.csproj index 00b7ce314..f99127c0e 100644 --- a/SyntaxVisitors/SyntaxVisitors.csproj +++ b/SyntaxVisitors/SyntaxVisitors.csproj @@ -59,6 +59,7 @@ + diff --git a/SyntaxVisitors/VisitorsForLambdas/RenameOnExceptVarsVisitor.cs b/SyntaxVisitors/VisitorsForLambdas/RenameOnExceptVarsVisitor.cs new file mode 100644 index 000000000..e3d0fe9ae --- /dev/null +++ b/SyntaxVisitors/VisitorsForLambdas/RenameOnExceptVarsVisitor.cs @@ -0,0 +1,90 @@ +// Copyright (c) Ivan Bondarev, Stanislav Mihalkovich (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 System.Text; +using System.Threading.Tasks; + +using PascalABCCompiler; +using PascalABCCompiler.SyntaxTree; + +namespace SyntaxVisitors +{ + // Проба будущего визитора или не будущего + public class RenameOnExceptVarsVisitor : BaseChangeVisitor + { + public string NameForRename = null; + public string NewName = null; + public RenameOnExceptVarsVisitor() + { + } + + public void Rename(exception_handler eh) + { + NameForRename = eh.variable?.name?.ToLower(); + if (NameForRename != null) + { + NewName = GetNewVariableName(NameForRename); + ProcessNode(eh.statements); + } + } + + public override void visit(ident id) + { + var idl = id.name.ToLower(); + if (NameForRename == idl) + { + id.name = NewName; + } + } + + public override void visit(dot_node dn) + { + ProcessNode(dn.left); + if (dn.right.GetType() != typeof(ident)) + ProcessNode(dn.right); + } + + private static int num = 0; + private static string GetNewVariableName(string name) + { + num++; + return "$Rename_"+name+num.ToString(); + } + } + + public class FindOnExceptVarsAndApplyRenameVisitor : BaseChangeVisitor + { + public static FindOnExceptVarsAndApplyRenameVisitor New + { + get { return new FindOnExceptVarsAndApplyRenameVisitor(); } + } + public override void visit(exception_handler eh) + { + if (eh.variable != null) + { + // Вначале переименовать везде кроме как в заголовке, потом добавить в блок операторов переприсваивание + var vis = new RenameOnExceptVarsVisitor(); + vis.Rename(eh); + + var vs = new var_statement(new ident(vis.NewName/*, eh.statements.source_context*/), new ident(vis.NameForRename, eh.variable.source_context)/*, eh.statements.source_context*/); + var stl = new List(); + stl.Add(vs); + if (eh.statements != null) + { + stl.Add(eh.statements); + ReplaceStatementUsingParent(eh.statements, stl); + } + else + { + var s = new statement_list(stl); + s.Parent = eh; + s.source_context = eh.source_context; + eh.statements = s; + } + } + base.visit(eh); + } + } +} diff --git a/SyntaxVisitors/YieldVisitors/RenameSameVarNamesVisitor.cs b/SyntaxVisitors/YieldVisitors/RenameSameVarNamesVisitor.cs new file mode 100644 index 000000000..0eef1ac24 --- /dev/null +++ b/SyntaxVisitors/YieldVisitors/RenameSameVarNamesVisitor.cs @@ -0,0 +1,181 @@ +// Copyright (c) Ivan Bondarev, Stanislav Mihalkovich (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.SyntaxTree; + +namespace PascalABCCompiler.SyntaxTreeConverters +{ + + // Первое предназначение - вынести последовательность из заголовка в foreach до foreach как отдельное присваивание + // Второе предназначение - переименовать все переменные, совпадающие по имени с типом T обобщенного класса, в котором находится метод, содержащий лямбду + public class VarNamesInMethodsWithSameNameAsClassGenericParamsReplacer: CollectLightSymInfoVisitor + { + /// + /// Надо приводить к нижнему регистру + /// + //public List NamesForReplace = new List(); + + private int LambdaNestedLevel = 0; + + Dictionary d = new Dictionary(); // словарь скоупов для каждого имени + // При входе в обобщенный класс или запись мы в словарь добавляем все его обобщенные параметры со значением nil + // При выходе из класса мы очищаем словарь + // При встрече описания переменной с именем, совпадающим с одним из ключей в словаре, мы заменяем значение null на пространство имен, в котором мы находимся + // Описание переменной в словарь добавляется только если она описана в методе, но вне лямбды, поскольку в лямбде конфликтов имен нет + // При выходе из пространства имен мы проверяем, есть ли в словаре такое значение, и если да, очищаем его, присваивая null + // Переименовывать будем все имена name если ключ name есть в словаре и d[name] != null + public static new VarNamesInMethodsWithSameNameAsClassGenericParamsReplacer New + { + get => new VarNamesInMethodsWithSameNameAsClassGenericParamsReplacer(); + } + public override void AddSymbol(ident name, SymKind kind, type_definition td = null, Attributes attr = 0) + { + if (name == null || name.name == null) + { + return; + } + var n = name.name.ToLower(); + //if (LambdaNestedLevel > 0) // т.е. впервые встретилось переопределение именно в лямбде, тогда пропускать эту лямбду + // return; + if (d.ContainsKey(n) && d[n] == null && (kind == SymKind.var || kind == SymKind.param)) // Добавляем не все, а только разыскиваемые и только если это - переменная + { + d[n] = this.Current; + base.AddSymbol(name, kind, td, attr); + } + } + public override void Enter(syntax_tree_node st) + { + base.Enter(st); + if (st is function_lambda_definition) + LambdaNestedLevel += 1; + else if (st is class_definition cl) + { + var td = cl.Parent as type_declaration; + if (td?.type_name is template_type_name ttn) + { + foreach (var id in ttn.template_args.idents) + { + d[id.name.ToLower()] = null; + } + } + } + else if (st is procedure_definition pd) + { + var cn = pd.proc_header.name?.class_name; + if (cn is template_type_name ttn) + { + foreach (var id in ttn.template_args.idents) + { + d[id.name.ToLower()] = null; + } + } + } + } + public override void PreExitScope(syntax_tree_node st) + { + var l = d.Keys.Where(k => d[k] == Current).ToList(); + // Обходим все словари и смотрим у них вершину стеков + foreach (var k in l) + { + d[k] = null; // т.е. в этом пространстве имен мы захватили одноименное описание переменной - освобождаем его + } + } + public override void Exit(syntax_tree_node st) + { + if (st is function_lambda_definition) + LambdaNestedLevel -= 1; + else if (st is class_definition) // то мы не ищем переопределение имен, поскольку мы вышли из класса + { + d.Clear(); + } + else if (st is procedure_definition pd) // мы вышли из метода вида t1.p + { + var cn = pd.proc_header.name?.class_name; + if (cn is template_type_name ttn) + d.Clear(); + } + base.Exit(st); + } + + public override void visit(template_type_name tn) + { + // игнорировать чтобы там ничего не переименовывалось + } + public override void visit(ident id) + { + ReplaceNameOrNot(id); + } + public override void visit(dot_node dn) + { + if (dn.left is ident id) + ReplaceNameOrNot(id); + else ProcessNode(dn.left); + } + + public static int ReplaceNum = 0; + public void ReplaceNameOrNot(ident id) + { + var n = id.name.ToLower(); + if (d.ContainsKey(n) && d[n] != null) // Переименовываем без страха + { + id.name = id.name + "$Replace$" + ReplaceNum; // имя такое чтобы при выдаче сообщения об ошибке в нем можно было бы обрезать конец + } + } + } + + // Может, переименовать все T? Всё равно алгоритм переименовывания будет один + // Искать описания с T. Как нашли - с этого места переименовывать + // Сделать визитор LocalRenamer и переименовывать с места описания + + /*public class LocalRenamer : BaseChangeVisitor + { + + public static void RenameFrom(var_def_statement vd, string Name, string NewName) + { + var lr = new LocalRenamer(); + lr.ProcessNode(vd); + } + }*/ + + + // Поиск всех захваченных переменных в лямбде + // Это сложно + /*public class LambdaCapturedNamesSearcher : BaseChangeVisitor + { + List idents = new List(); + public static LambdaCapturedNamesSearcher New + { + get + { + return new LambdaCapturedNamesSearcher(); + } + } + public override void visit(ident id) + { + // хорошо бы всё здесь захватить! Но вдруг это - описание... + } + public override void visit(dot_node dn) + { + // это точно надо захватывать + } + + public override void visit(var_def_statement vds) + { + // имена обойти отдельно, инициализатор - отдельно + foreach (var id in vds.vars.idents) + { + // Надо проверять совпадение имен с именами в параметрах обобщения + // если не совпадают, то просто пропускаем + // если совпадают, то исключаем это имя из списка проверки! Всё - имя уже переопределено! И это произошло в лямбде, что можно + } + visit(vds.inital_value); + } + public override void visit(function_lambda_definition ld) + { + // параметры обойти отдельно, тело отдельно + // проблема, что могут быть вложенные лямбды. Т.е это - основная, а в ней - ещё. + } + }*/ +} diff --git a/TestSuite/lambda_OnExcept.pas b/TestSuite/lambda_OnExcept.pas new file mode 100644 index 000000000..bba0fdb7f --- /dev/null +++ b/TestSuite/lambda_OnExcept.pas @@ -0,0 +1,37 @@ +type MyException = class(Exception) end; + +procedure p1; +begin + + var i := 8; + var p: procedure := ()-> + begin + i := 3; + end; + + try + raise new MyException; + except + on e: Exception do + begin + var q: procedure := ()-> + begin + Print(e); + end; + //q; + end; + on e: MyException do + begin + var q: procedure := ()-> + begin + Print(e); + end; + //q; + end; + end; + +end; + +begin + p1; +end. \ No newline at end of file diff --git a/TreeConverter/LambdaExpressions/Closure/CapturedVariablesTreeBuilder.cs b/TreeConverter/LambdaExpressions/Closure/CapturedVariablesTreeBuilder.cs index d473dcd03..0982684f8 100644 --- a/TreeConverter/LambdaExpressions/Closure/CapturedVariablesTreeBuilder.cs +++ b/TreeConverter/LambdaExpressions/Closure/CapturedVariablesTreeBuilder.cs @@ -144,16 +144,23 @@ namespace TreeConverter.LambdaExpressions.Closure public override void visit(exception_handler eh) { - //_visitor.context.enter_code_block_without_bind(); - //visitor.context.add_var_definition(eh.variable.name, _visitor.get_location(eh.variable), _visitor.convert_strong(eh.type_name), PascalABCCompiler.SemanticTree.polymorphic_state.ps_common, true); + // Отдельным визитором переменные в on присвою локальным переменным и переименую везде внутри блока + statements_list sl = new statements_list(_visitor.get_location(eh.statements)); + _visitor.convertion_data_and_alghoritms.statement_list_stack_push(sl); + + _visitor.context.enter_code_block_without_bind(); + + _visitor.context.add_var_definition(eh.variable.name, _visitor.get_location(eh.variable), _visitor.convert_strong(eh.type_name), PascalABCCompiler.SemanticTree.polymorphic_state.ps_common, true); //SymbolInfo si = _visitor.context.find_first(eh.variable.name); //var csi = new CapturedVariablesTreeNode.CapturedSymbolInfo(eh, si); - // Нужно своё ПИ для обработчиков исключений в захвате !!! Или - переименовывать все переменные - // _currentTreeNode.VariablesDefinedInScope.Add(new CapturedVariablesTreeNode.CapturedSymbolInfo(eh, si)); + //_currentTreeNode.VariablesDefinedInScope.Add(new CapturedVariablesTreeNode.CapturedSymbolInfo(eh, si)); //_pendingCapturedSymbols.Add(csi); + ProcessNode(eh.variable); + ProcessNode(eh.statements); + _visitor.context.leave_code_block(); + _visitor.convertion_data_and_alghoritms.statement_list_stack_pop(); //_pendingCapturedSymbols.Remove(csi); - //_visitor.context.leave_code_block(); } public override void visit(ident id)