fix #1814
This commit is contained in:
parent
37b4d894af
commit
bf2a770ad5
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
%MINOR%=5
|
||||
%REVISION%=2157
|
||||
%REVISION%=2165
|
||||
%COREVERSION%=0
|
||||
%MAJOR%=3
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
!define VERSION '3.5.0.2157'
|
||||
!define VERSION '3.5.0.2165'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@
|
|||
<Compile Include="SugarVisitors\PatternsDesugaringVisitor.cs" />
|
||||
<Compile Include="SugarVisitors\SliceDesugarVisitor.cs" />
|
||||
<Compile Include="SugarVisitors\TupleVisitor.cs" />
|
||||
<Compile Include="VisitorsForLambdas\RenameOnExceptVarsVisitor.cs" />
|
||||
<Compile Include="VisitorsForLambdas\RenameSameVarNamesVisitor.cs" />
|
||||
<Compile Include="VisitorsForLambdas\StandOutExprWithLambdaInForeachSequenceVisitor.cs" />
|
||||
<Compile Include="YieldVisitors\ObjectCopier\ObjectCopier.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<statement>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
181
SyntaxVisitors/YieldVisitors/RenameSameVarNamesVisitor.cs
Normal file
181
SyntaxVisitors/YieldVisitors/RenameSameVarNamesVisitor.cs
Normal file
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Надо приводить к нижнему регистру
|
||||
/// </summary>
|
||||
//public List<string> NamesForReplace = new List<string>();
|
||||
|
||||
private int LambdaNestedLevel = 0;
|
||||
|
||||
Dictionary<string, ScopeSyntax> d = new Dictionary<string, ScopeSyntax>(); // словарь скоупов для каждого имени
|
||||
// При входе в обобщенный класс или запись мы в словарь добавляем все его обобщенные параметры со значением 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<t>.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<ident> idents = new List<ident>();
|
||||
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)
|
||||
{
|
||||
// параметры обойти отдельно, тело отдельно
|
||||
// проблема, что могут быть вложенные лямбды. Т.е это - основная, а в ней - ещё.
|
||||
}
|
||||
}*/
|
||||
}
|
||||
37
TestSuite/lambda_OnExcept.pas
Normal file
37
TestSuite/lambda_OnExcept.pas
Normal file
|
|
@ -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.
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue