pascalabcnet/SyntaxVisitors/YieldVisitors/RenameSameBlockLocalVarsVisitor.cs

264 lines
10 KiB
C#
Raw Permalink Normal View History

// 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 System.Text;
using System.Threading.Tasks;
using PascalABCCompiler;
using PascalABCCompiler.SyntaxTree;
namespace SyntaxVisitors
{
public class RenameSameBlockLocalVarsVisitor : BaseChangeVisitor
{
// Надо хранить принадлежность имени конкретному ПИ на определенном уровне вложенности
// И отображение этого имени в новое
// Map :: CurrentLevel -> { BlockName -> NewBlockName }
private List<Dictionary<string, string>> BlockNamesStack { get; set; }
private Dictionary<string, int> BlockNamesCounter { get; set; }
private int CurrentLevel = -1;
public RenameSameBlockLocalVarsVisitor()
{
this.BlockNamesStack = new List<Dictionary<string, string>>();
this.BlockNamesCounter = new Dictionary<string, int>();
}
public static RenameSameBlockLocalVarsVisitor New
{
get { return new RenameSameBlockLocalVarsVisitor(); }
}
public static void Accept(procedure_definition pd)
{
New.ProcessNode(pd);
}
public override void visit(declarations decls)
{
// Для обхода в правильном порядке сверху вниз
var vd_s = decls.defs.OfType<variable_definitions>().ToArray();
for (int i = 0; i < vd_s.Count(); ++i)
{
ProcessNode(vd_s[i]);
}
}
2016-08-01 18:40:49 +03:00
public override void visit(procedure_header proc_header)
{
2016-08-01 18:40:49 +03:00
// DO THIS THING!
}
2016-08-01 18:40:49 +03:00
private bool first_time_visit_function_header = true;
public override void visit(function_header fh)
{
2016-08-01 18:40:49 +03:00
if (first_time_visit_function_header)
{
2019-07-11 22:49:48 +03:00
//DefaultVisit(fh);
2016-08-01 18:40:49 +03:00
first_time_visit_function_header = false;
}
// DO NOTHING
}
public override void visit(formal_parameters fp)
{
2016-08-01 18:40:49 +03:00
if (!first_time_visit_function_header) // чтобы в лямбдах не заходить в формальные параметры. Во вложенных тоже не зайдёт
return;
2016-08-06 15:23:20 +03:00
// SSM переименование формальных параметров (нужно если мы их изменяем внутри)
2016-08-01 18:40:49 +03:00
++CurrentLevel;
BlockNamesStack.Add(new Dictionary<string, string>());
if (fp != null)
{
var fpids = fp.params_list.SelectMany(tp => tp.idents.idents);
foreach (var v in fpids)
{
2019-07-06 01:10:31 +03:00
var low = v.name.ToLower();
2017-05-01 17:08:26 +03:00
//BlockNamesStack[CurrentLevel].Add(low, "$fp_"+ low);
BlockNamesStack[CurrentLevel][low] = "$fp_" + low;
2016-08-01 18:40:49 +03:00
}
}
// DO NOTHING
}
public override void visit(statement_list stlist)
{
++CurrentLevel;
if (BlockNamesStack.Count <= CurrentLevel)
{
// Создаем отображение для имен текущего уровня вложенности мини-пространства имен
BlockNamesStack.Add(new Dictionary<string, string>());
}
//base.visit(stlist);
for (var i = 0; i < stlist.list.Count; ++i)
ProcessNode(stlist.list[i]);
BlockNamesStack.RemoveAt(BlockNamesStack.Count - 1);
--CurrentLevel;
}
2018-11-08 00:24:28 +03:00
public override void visit(var_def_statement var_def)
{
2018-11-08 00:24:28 +03:00
if (var_def.vars.idents.Any(id => id.name.StartsWith("$")))
{
2018-11-08 00:24:28 +03:00
base.visit(var_def); // SSM 17/07/16 исправление ошибки - не обходилось выражение-инициализатор
return;
}
2024-06-09 13:30:20 +03:00
var newNameCache = new Dictionary<string, string>();
2018-11-08 00:24:28 +03:00
var newLocalNames = var_def.vars.idents.Select(id =>
{
2019-07-06 01:10:31 +03:00
var low = id.name.ToLower();
2017-05-01 17:08:26 +03:00
var newName = this.CreateNewVariableName(low);
//BlockNamesStack[CurrentLevel].Add(low, newName);
2024-06-09 13:30:20 +03:00
//BlockNamesStack[CurrentLevel][low] = newName;
newNameCache[low] = newName;
return new ident(newName, id.source_context);
});
2018-11-08 00:24:28 +03:00
var newVS = new var_def_statement(new ident_list(newLocalNames.ToArray()),
var_def.vars_type,
var_def.inital_value);
Replace(var_def, newVS);
listNodes[listNodes.Count - 1] = newVS; //SSM 8.11.18
2024-06-09 13:10:43 +03:00
ProcessNode(newVS.inital_value); // SSM 10.06.2020 #2103
2024-06-09 13:30:20 +03:00
foreach (var key in newNameCache.Keys)
BlockNamesStack[CurrentLevel][key] = newNameCache[key];
2020-06-10 09:09:56 +03:00
//base.visit(newVS); // SSM 10.06.2020 - зачем обходить всё?
2018-11-08 00:24:28 +03:00
}
/*public override void visit(var_statement vs)
{
if (vs.var_def.vars.idents.Any(id => id.name.StartsWith("$")))
{
base.visit(vs); // SSM 17/07/16 исправление ошибки - не обходилось выражение-инициализатор
return;
}
var newLocalNames = vs.var_def.vars.idents.Select(id =>
{
var low = id.name
//.ToLower()
;
var newName = this.CreateNewVariableName(low);
//BlockNamesStack[CurrentLevel].Add(low, newName);
BlockNamesStack[CurrentLevel][low] = newName;
return new ident(newName, id.source_context);
});
var newVS = new var_statement(new var_def_statement(new ident_list(newLocalNames.ToArray()),
vs.var_def.vars_type,
vs.var_def.inital_value));
Replace(vs, newVS);
base.visit(newVS);
2018-11-08 00:24:28 +03:00
}/* */
public override void visit(variable_definitions vd)
{
if (vd.var_definitions.Any(vds => vds.vars.idents.Any(id => id.name.StartsWith("$"))))
return;
++CurrentLevel;
if (BlockNamesStack.Count <= CurrentLevel)
{
// Создаем отображение для имен текущего уровня вложенности мини-пространства имен
BlockNamesStack.Add(new Dictionary<string, string>());
}
var newVD = new variable_definitions(
vd.var_definitions.Select(vds =>
{
var newLocalNames = vds.vars.idents.Select(id =>
{
2019-07-06 01:10:31 +03:00
var low = id.name.ToLower();
2017-05-01 17:08:26 +03:00
var newName = this.CreateNewVariableName(low);
//BlockNamesStack[CurrentLevel].Add(low, newName);
BlockNamesStack[CurrentLevel][low] = newName;
return new ident(newName, id.source_context);
});
return new var_def_statement(new ident_list(newLocalNames.ToArray()), vds.vars_type, vds.inital_value);
}).ToList(),
vd.source_context);
Replace(vd, newVD);
base.visit(newVD);
}
public override void visit(ident id)
{
2020-06-25 14:51:55 +03:00
// Это очень частный алгоритм переименования в мини-ПИ, но затрагивает он и лямбды. И механизм для этого - неадекватный
// Надо исключать формальные параметры лямбд и одноимённые переменнные, определяемые у них внутри
2019-07-06 01:10:31 +03:00
var newName = this.GetNewVariableName(id.name.ToLower());
2017-05-01 17:08:26 +03:00
if (newName != null)
{
Replace(id, new ident(newName, id.source_context));
}
}
public override void visit(dot_node dn)
{
ProcessNode(dn.left);
if (dn.right.GetType() != typeof(ident))
ProcessNode(dn.right);
}
2020-06-25 14:51:55 +03:00
public override void visit(function_lambda_definition fld)
{
BlockNamesStack.Add(new Dictionary<string, string>());
var last = BlockNamesStack.Count - 1;
for (var i=0; i< fld.parameters.expressions.Count; i++)
{
var qname = (fld.parameters.expressions[i] as ident).name.ToLower();
BlockNamesStack[last][qname] = "-fl"; // -fl - формальный параметр лямбды - это стоп в поиске в GetNewVariableName
}
base.visit(fld);
}
private string CreateNewVariableName(string name)
{
2019-07-06 01:10:31 +03:00
name = name.ToLower();
if (BlockNamesCounter.ContainsKey(name))
{
++BlockNamesCounter[name];
}
else
{
BlockNamesCounter.Add(name, 0);
}
return "$" + name + "__" + BlockNamesCounter[name];
}
private string GetNewVariableName(string name)
{
2019-07-06 01:10:31 +03:00
name = name.ToLower();
for (int i = CurrentLevel; i >= 0; --i)
{
if (BlockNamesStack[i].ContainsKey(name))
{
2020-06-25 14:51:55 +03:00
if (BlockNamesStack[i][name] == "-fl") // формальный параметр лямбды - это стоп в поиске (переименовывать не надо!)
return null;
return BlockNamesStack[i][name];
}
}
return null;
}
}
}