diff --git a/SyntaxVisitors/PatternsVisitors/ReplaceNamesInIsVarVisitor.cs b/SyntaxVisitors/PatternsVisitors/ReplaceNamesInIsVarVisitor.cs new file mode 100644 index 000000000..354179ea7 --- /dev/null +++ b/SyntaxVisitors/PatternsVisitors/ReplaceNamesInIsVarVisitor.cs @@ -0,0 +1,200 @@ +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 NamesInIsVars = new HashSet(); + 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 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 NamesToRename = null; + // Список (стек) пространств имен + List nsp = new List(); + // Словарь имён + Dictionary> dict = new Dictionary>(); + 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(); + 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); + } + + } +}