201 lines
7.5 KiB
C#
201 lines
7.5 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Text;
|
|||
|
|
using PascalABCCompiler.SyntaxTree;
|
|||
|
|
|
|||
|
|
|
|||
|
|
// SSM 13/07/20
|
|||
|
|
// Нигде не используется поскольку не решает проблему конфликта is_var и yield.
|
|||
|
|
// Хотя видимо все другие проблемы может решать
|
|||
|
|
namespace SyntaxVisitors.PatternsVisitors
|
|||
|
|
{
|
|||
|
|
class CollectNamesInIsVarVisitor : WalkingVisitorNew
|
|||
|
|
{
|
|||
|
|
public HashSet<string> NamesInIsVars = new HashSet<string>();
|
|||
|
|
public static CollectNamesInIsVarVisitor New => new CollectNamesInIsVarVisitor();
|
|||
|
|
|
|||
|
|
public override void visit(is_pattern_expr isPatternExpr)
|
|||
|
|
{
|
|||
|
|
var pp = isPatternExpr.right?.parameters;
|
|||
|
|
if (pp != null)
|
|||
|
|
{
|
|||
|
|
foreach (var p in pp)
|
|||
|
|
{
|
|||
|
|
if (p is var_deconstructor_parameter vdp)
|
|||
|
|
{
|
|||
|
|
NamesInIsVars.Add(vdp.identifier.name.ToLower());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static HashSet<string> NamesToRename(procedure_definition pd)
|
|||
|
|
{
|
|||
|
|
var v = new CollectNamesInIsVarVisitor();
|
|||
|
|
v.ProcessNode(pd);
|
|||
|
|
return v.NamesInIsVars;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
struct PairNames
|
|||
|
|
{
|
|||
|
|
public ident Name;
|
|||
|
|
public string NewName;
|
|||
|
|
public PairNames(ident Name, string NewName)
|
|||
|
|
{
|
|||
|
|
this.Name = Name;
|
|||
|
|
this.NewName = NewName;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Этот визитор запускать только с procedure_definition: ReplaceNamesInIsVarVisitor.New.ProcessNode(pd)
|
|||
|
|
class ReplaceNamesInIsVarVisitor : WalkingVisitorNew
|
|||
|
|
{
|
|||
|
|
HashSet<string> NamesToRename = null;
|
|||
|
|
// Список (стек) пространств имен
|
|||
|
|
List<syntax_tree_node> nsp = new List<syntax_tree_node>();
|
|||
|
|
// Словарь имён
|
|||
|
|
Dictionary<syntax_tree_node, List<PairNames>> dict = new Dictionary<syntax_tree_node, List<PairNames>>();
|
|||
|
|
private int num = 0;
|
|||
|
|
public string GenerateNewName(string name)
|
|||
|
|
{
|
|||
|
|
num += 1;
|
|||
|
|
return "#GenIsVar" + num + "#" + name;
|
|||
|
|
}
|
|||
|
|
public static ReplaceNamesInIsVarVisitor New => new ReplaceNamesInIsVarVisitor();
|
|||
|
|
|
|||
|
|
public syntax_tree_node CurrentNamespace() => nsp[nsp.Count - 1];
|
|||
|
|
|
|||
|
|
public void PushNamespace(syntax_tree_node stn)
|
|||
|
|
{
|
|||
|
|
dict[stn] = new List<PairNames>();
|
|||
|
|
nsp.Add(stn);
|
|||
|
|
}
|
|||
|
|
public void PopNamespace()
|
|||
|
|
{
|
|||
|
|
// удалить соответствующий элемент словаря - если он есть
|
|||
|
|
dict.Remove(CurrentNamespace());
|
|||
|
|
nsp.RemoveAt(nsp.Count - 1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void AddNameNewName(ident name, string newname)
|
|||
|
|
{
|
|||
|
|
if (!NamesToRename.Contains(name.name.ToLower())) // Если имя - не из нашего внимания, то не добавляем
|
|||
|
|
return;
|
|||
|
|
// Ошибка возникает когда это имя есть в каком-то namespace выше и оно - не через границу function_lambda_definition
|
|||
|
|
// Начинаем проверять с текущего и выше до f_l_d включительно или до p_d включительно. Если есть совпадение, то ошибка
|
|||
|
|
var i = nsp.Count - 1;
|
|||
|
|
while (true)
|
|||
|
|
{
|
|||
|
|
var curns = nsp[i];
|
|||
|
|
// проверить, есть ли в dict[curns] такое имя
|
|||
|
|
if (dict[curns].Select(n=>n.Name.name.ToLower()).Contains(name.name.ToLower()))
|
|||
|
|
throw new SyntaxVisitorError("Повторное объявление", name.source_context);
|
|||
|
|
if (curns is procedure_definition || curns is function_lambda_definition)
|
|||
|
|
break;
|
|||
|
|
i--;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
dict[CurrentNamespace()].Add(new PairNames(name, newname));
|
|||
|
|
}
|
|||
|
|
public override void visit(procedure_definition pd) // три вида ПИ, которые надо учитывать
|
|||
|
|
{
|
|||
|
|
NamesToRename = CollectNamesInIsVarVisitor.NamesToRename(pd); // Это - акцент внимания
|
|||
|
|
if (NamesToRename.Count == 0)
|
|||
|
|
return; // nothing top do
|
|||
|
|
|
|||
|
|
PushNamespace(pd);
|
|||
|
|
|
|||
|
|
if (pd.proc_header.parameters != null)
|
|||
|
|
{
|
|||
|
|
var allnames = pd.proc_header.parameters.params_list.SelectMany(tp => tp.idents.idents);
|
|||
|
|
foreach (var name in allnames)
|
|||
|
|
AddNameNewName(name, null); // null означает, что переименовывать не надо
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
base.visit(pd);
|
|||
|
|
PopNamespace();
|
|||
|
|
NamesToRename = null;
|
|||
|
|
}
|
|||
|
|
public override void visit(function_lambda_definition fd)
|
|||
|
|
{
|
|||
|
|
PushNamespace(fd);
|
|||
|
|
if (fd.formal_parameters != null)
|
|||
|
|
{
|
|||
|
|
var allnames = fd.formal_parameters.params_list.SelectMany(tp => tp.idents.idents);
|
|||
|
|
foreach (var name in allnames)
|
|||
|
|
AddNameNewName(name, null); // null означает, что переименовывать не надо
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
base.visit(fd);
|
|||
|
|
PopNamespace();
|
|||
|
|
}
|
|||
|
|
public override void visit(statement_list stl)
|
|||
|
|
{
|
|||
|
|
PushNamespace(stl);
|
|||
|
|
base.visit(stl);
|
|||
|
|
PopNamespace();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public override void visit(is_pattern_expr isPatternExpr)
|
|||
|
|
{
|
|||
|
|
var pp = isPatternExpr.right?.parameters;
|
|||
|
|
if (pp != null)
|
|||
|
|
{
|
|||
|
|
foreach (var p in pp)
|
|||
|
|
{
|
|||
|
|
if (p is var_deconstructor_parameter vdp)
|
|||
|
|
{
|
|||
|
|
var newname = GenerateNewName(vdp.identifier.name);
|
|||
|
|
AddNameNewName(vdp.identifier.TypedClone(), newname);
|
|||
|
|
vdp.identifier.name = newname;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// попытаться переименовать
|
|||
|
|
public void TryToRename(ident id)
|
|||
|
|
{
|
|||
|
|
if (!NamesToRename.Contains(id.name.ToLower()))
|
|||
|
|
return;
|
|||
|
|
// Начинаем искать по ПИ это имя с именем для переименования
|
|||
|
|
for (var i = nsp.Count - 1; i >= 0; i--)
|
|||
|
|
{
|
|||
|
|
var curns = nsp[i];
|
|||
|
|
// проверить, есть ли в dict[curns] такое имя
|
|||
|
|
var ind = dict[curns].FindIndex(pn => pn.Name.name.ToLower() == id.name.ToLower());
|
|||
|
|
if (ind != -1)
|
|||
|
|
{
|
|||
|
|
var newname = dict[curns][ind].NewName;
|
|||
|
|
if (newname != null)
|
|||
|
|
{
|
|||
|
|
id.name = newname;
|
|||
|
|
return; //переименовали - миссия выполнена!
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (curns is procedure_definition || curns is function_lambda_definition)
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Собственно переименование
|
|||
|
|
public override void visit(ident id)
|
|||
|
|
{
|
|||
|
|
TryToRename(id);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public override void visit(dot_node dn)
|
|||
|
|
{
|
|||
|
|
if (dn.left is ident id)
|
|||
|
|
TryToRename(id);
|
|||
|
|
else ProcessNode(dn.left);
|
|||
|
|
ProcessNode(dn.right);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}
|