From 133d082e4e2cbec81ca8d0ab8fd6f40b54fc08f5 Mon Sep 17 00:00:00 2001 From: nevermind322 <81963698+nevermind322@users.noreply.github.com> Date: Thu, 6 Jun 2024 23:10:38 +0300 Subject: [PATCH] Assign tuple optimizer (#3142) * branch init * add BindCollectLightSymInfo * moved lightsyminfo to syntaxVisitors * LIghtSymInfo fixes * binder 0.1 * Attributes -> SymbolAttributes * AssignTupleOptimizer -> AssignTupleDesugar * moved algorithm classes to AssignTupleDesugarAlgorithm namespace * fixes and refactoring * LightSymInfo rework * add rebuildDebug and build units * enable VarNamesInMethodsWithSameNameAsClassGenericParamsReplacer * remove CollectFullLightSymInfo call * bind now return BindResult * change BindResult path type * path testing * fix Current in BindCollectLightSymInfo * fix Current in BindCollectLightSymInfo * potential symomyms handling without assigns count minimization * assign count minimization * rename AssignTuplesDesugar * move NewAssignTupleDesugar * add optimization switch * delete AssignTupleDesugar project * remove comments --- .../StandardSyntaxConverter.cs | 12 +- PascalABCNET.sln | 4 +- .../BindCollectLightSymInfo.cs | 17 +- .../LightScopeHelperClasses.cs | 15 +- .../AssignTuplesDesugarVisitor.cs | 0 .../NewAssignTuplesDesugarVisitor.cs | 61 +++ .../desugarAlgoritm/Assign.cs | 45 ++ .../desugarAlgoritm/AssignGraph.cs | 166 ++++++ .../desugarAlgoritm/Cycle.cs | 31 ++ .../desugarAlgoritm/Edge.cs | 30 + .../desugarAlgoritm/GraphUtils.cs | 512 ++++++++++++++++++ .../desugarAlgoritm/Symbol.cs | 129 +++++ .../desugarAlgoritm/SymbolNode.cs | 67 +++ SyntaxVisitors/SugarVisitors/TupleVisitor.cs | 92 ++-- SyntaxVisitors/SyntaxVisitors.csproj | 11 +- bin/AssignTupleOptimizer.dll | Bin 0 -> 23040 bytes 16 files changed, 1133 insertions(+), 59 deletions(-) rename SyntaxVisitors/SugarVisitors/{ => AssignTuplesDesugarVisitor}/AssignTuplesDesugarVisitor.cs (100%) create mode 100644 SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/NewAssignTuplesDesugarVisitor.cs create mode 100644 SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/Assign.cs create mode 100644 SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/AssignGraph.cs create mode 100644 SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/Cycle.cs create mode 100644 SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/Edge.cs create mode 100644 SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/GraphUtils.cs create mode 100644 SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/Symbol.cs create mode 100644 SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/SymbolNode.cs create mode 100644 bin/AssignTupleOptimizer.dll diff --git a/Languages/Pascal/SyntaxTreeConverters/SyntaxTreeConverters/StandardSyntaxConverter.cs b/Languages/Pascal/SyntaxTreeConverters/SyntaxTreeConverters/StandardSyntaxConverter.cs index 88631da96..263b6d359 100644 --- a/Languages/Pascal/SyntaxTreeConverters/SyntaxTreeConverters/StandardSyntaxConverter.cs +++ b/Languages/Pascal/SyntaxTreeConverters/SyntaxTreeConverters/StandardSyntaxConverter.cs @@ -15,7 +15,9 @@ namespace Languages.Pascal.Frontend.Converters protected override syntax_tree_node ApplyConcreteConversions(syntax_tree_node root) { + root.FillParentsInAllChilds(); + var binder = new BindCollectLightSymInfo(root as compilation_unit); #if DEBUG // var stat = new ABCStatisticsVisitor(); // stat.ProcessNode(root); @@ -42,9 +44,9 @@ namespace Languages.Pascal.Frontend.Converters #if DEBUG //new SimplePrettyPrinterVisitor("D:/out.txt").ProcessNode(root); #endif - + bool optimize_tuple_assign = true; // tuple_node - TupleVisitor.New.ProcessNode(root); + TupleVisitor.Create(optimize_tuple_assign).ProcessNode(root); // index IndexVisitor.New.ProcessNode(root); @@ -55,8 +57,10 @@ namespace Languages.Pascal.Frontend.Converters // теперь коллизия с (a[1:6], a[6:11]):= (a[6:11], a[1:6]); // assign_tuple и assign_var_tuple - AssignTuplesDesugarVisitor.New.ProcessNode(root); // теперь это - на семантике - + if (!optimize_tuple_assign) + AssignTuplesDesugarVisitor.New.ProcessNode(root); // теперь это - на семантике + else + NewAssignTuplesDesugarVisitor.Create(binder).ProcessNode(root); // question_point_desugar_visitor QuestionPointDesugarVisitor.New.ProcessNode(root); diff --git a/PascalABCNET.sln b/PascalABCNET.sln index c1f52f38b..3d7567d26 100644 --- a/PascalABCNET.sln +++ b/PascalABCNET.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.8.34309.116 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.34301.259 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VisualPascalABCNET", "VisualPascalABCNET", "{26843C5D-9D7E-4C2C-AC14-2D227FA5592E}" EndProject diff --git a/SyntaxVisitors/LightSymInfoVisitors/BindCollectLightSymInfo.cs b/SyntaxVisitors/LightSymInfoVisitors/BindCollectLightSymInfo.cs index 4f58774cf..21a0d77b8 100644 --- a/SyntaxVisitors/LightSymInfoVisitors/BindCollectLightSymInfo.cs +++ b/SyntaxVisitors/LightSymInfoVisitors/BindCollectLightSymInfo.cs @@ -1,4 +1,5 @@ - +using System.Collections.Generic; +using System.Linq; namespace PascalABCCompiler.SyntaxTree { @@ -12,22 +13,23 @@ namespace PascalABCCompiler.SyntaxTree { Current = Root; visit(root); + Current = null; } - public SymInfoSyntax bind(ident node) + public BindResult bind(ident node) { - ///Console.WriteLine("searching " + node.ToString()); + + Current = null; syntax_tree_node cur_node = node; + var path = new Queue(); while (cur_node != null) { - // Console.WriteLine("cur_node: " + cur_node.ToString()); if (AbstractScopeCreator.IsScopeCreator(cur_node)) { - //Console.WriteLine("checking scope" + cur_node.ToString()) var cur_scope = scopeCreator.GetScope(cur_node); - //Console.WriteLine(cur_scope.ToString() + ": " + cur_scope.Symbols.Count); + path.Enqueue(cur_scope); var prev_scope = Current; if (prev_scope != null) @@ -36,11 +38,10 @@ namespace PascalABCCompiler.SyntaxTree Current = cur_scope; if (cur_scope.Parent == null && !(Current is GlobalScopeSyntax)) { - //Console.WriteLine("visiting " + cur_node.ToString()); cur_node.visit(this); } var res = Current.bind(node); - if (res != null) return res; + if (res != null) return new BindResult(res, path.ToList()); } cur_node = cur_node.Parent; } diff --git a/SyntaxVisitors/LightSymInfoVisitors/LightScopeHelperClasses.cs b/SyntaxVisitors/LightSymInfoVisitors/LightScopeHelperClasses.cs index 6b163c14d..b16d36a03 100644 --- a/SyntaxVisitors/LightSymInfoVisitors/LightScopeHelperClasses.cs +++ b/SyntaxVisitors/LightSymInfoVisitors/LightScopeHelperClasses.cs @@ -22,7 +22,7 @@ namespace PascalABCCompiler.SyntaxTree recordname, interfacename, template_param, type_alias, const_var, property }; [Flags] - public enum SymbolAttributes { class_attr = 1, varparam_attr = 2, private_attr = 4}; + public enum SymbolAttributes { class_attr = 1, varparam_attr = 2, private_attr = 4 }; public class SymInfoSyntax { @@ -53,6 +53,19 @@ namespace PascalABCCompiler.SyntaxTree } } + public class BindResult { + + public SymInfoSyntax symInfo; + public List path; + + public BindResult(SymInfoSyntax s, List p) + { + symInfo = s; + path = p; + } + + } + public abstract class ScopeSyntax { public syntax_tree_node node; diff --git a/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor.cs b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/AssignTuplesDesugarVisitor.cs similarity index 100% rename from SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor.cs rename to SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/AssignTuplesDesugarVisitor.cs diff --git a/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/NewAssignTuplesDesugarVisitor.cs b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/NewAssignTuplesDesugarVisitor.cs new file mode 100644 index 000000000..681946505 --- /dev/null +++ b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/NewAssignTuplesDesugarVisitor.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; +using System.Linq; +using AssignTupleDesugarAlgorithm; +using PascalABCCompiler.SyntaxTree; + +namespace SyntaxVisitors.SugarVisitors +{ + public class NewAssignTuplesDesugarVisitor : AssignTuplesDesugarVisitor + { + + private BindCollectLightSymInfo binder; + + + public static NewAssignTuplesDesugarVisitor Create(BindCollectLightSymInfo binder) + { + return new NewAssignTuplesDesugarVisitor(binder); + } + + public NewAssignTuplesDesugarVisitor(BindCollectLightSymInfo binder) + { + this.binder = binder; + } + + + List desugar(tuple_node tn, addressed_value_list vars) + { + var order = Assign.getAssignOrder(tn, vars, binder); + var assigns = new List(); + foreach (var a in order) + { + if (a.to is TempSymbol ts) + { + var cur = new var_def_statement(ts.node as ident, a.from.node, tn.Parent.source_context); + assigns.Add(new var_statement(cur, tn.Parent.source_context)); + } + else + { + var cur = new assign(a.to.node as addressed_value, a.from.node, tn.Parent.source_context); + assigns.Add(cur); + } + } + return assigns; + } + + + public override void visit(assign_tuple node) + { + if (node.expr is tuple_node tn) + { + var n = node.vars.variables.Count(); + if (n > tn.el.Count) + throw new SyntaxVisitorError("TOO_MANY_ELEMENTS_ON_LEFT_SIDE_OF_TUPLE_ASSIGNMENT", node.vars.variables[0]); + + var assigns = desugar(tn, node.vars); + ReplaceStatementUsingParent(node, assigns); + } + else + base.visit(node); + } + } +} diff --git a/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/Assign.cs b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/Assign.cs new file mode 100644 index 000000000..9e7bd4213 --- /dev/null +++ b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/Assign.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Linq; +using PascalABCCompiler.SyntaxTree; + +namespace AssignTupleDesugarAlgorithm +{ + public class Assign + { + public Symbol from; + public Symbol to; + public Assign(Symbol to, Symbol from) + { + this.to = to; + this.from = from; + } + + public static List getAssignOrder(tuple_node tn, addressed_value_list vars, BindCollectLightSymInfo binder) + { + var left = new List(); + var right = new List(); + + foreach (var ex in vars.variables) + { + var s = new Symbol(ex, binder); + left.Add(s); + } + + + foreach (var ex in tn.el.expressions) + { + var s = new Symbol(ex, binder); + right.Add(s); + } + + + var graph = GraphUtils.createAssignGraph(left, right); + //graph.drawGraph(); + var order = graph.GetAssignOrder(); + return order.Select(elem => new Assign(to: elem.to.symbol, from: elem.from.symbol)).ToList(); + } + + + + } +} diff --git a/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/AssignGraph.cs b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/AssignGraph.cs new file mode 100644 index 000000000..eb3cda1ab --- /dev/null +++ b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/AssignGraph.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace AssignTupleDesugarAlgorithm +{ + internal class AssignGraph : IEnumerable + { + public readonly HashSet vertexes = new HashSet(); + + public readonly List edges = new List(); + private bool AdjStructuresNeedSync = false; + + public List assignOrder = new List(); + public List assignLast = new List(); + public List assignFirst = new List(); + + private Dictionary> outAdjStructure = null; + + public Dictionary> OutAdjStructure + { + get + { + if (outAdjStructure == null || AdjStructuresNeedSync) + { + outAdjStructure = createOutAdjStructure(); + inAdjStructure = createInAdjStructure(); + AdjStructuresNeedSync = false; + } + + return outAdjStructure; + } + } + + private Dictionary> createOutAdjStructure() + { + Dictionary> res = new Dictionary>(); + + foreach (var vert in vertexes) res.Add(vert, new List()); + + foreach (Edge edge in edges) res[edge.from].Add(edge.to); + + return res; + } + + private Dictionary> inAdjStructure = null; + + public Dictionary> InAdjStructure + { + get + { + if (inAdjStructure == null || AdjStructuresNeedSync) + { + outAdjStructure = createOutAdjStructure(); + inAdjStructure = createInAdjStructure(); + AdjStructuresNeedSync = false; + } + + return inAdjStructure; + } + } + + private Dictionary> createInAdjStructure() + { + Dictionary> res = new Dictionary>(); + + foreach (var vert in vertexes) res.Add(vert, new List()); + + foreach (Edge edge in edges) res[edge.to].Add(edge.from); + + return res; + } + + + public List GetInEdgesForVertex(SymbolNode v) => edges.FindAll(edge => edge.to == v); + public List GetOutEdgesForVertex(SymbolNode v) => edges.FindAll(edge => edge.from == v); + + public void resetVertexesColor() + { + foreach (SymbolNode v in vertexes) v.resetColor(); + } + + public bool EnsureThatEveryVertexHasOneOrZeroInEdge() => + vertexes.All(vert => GetInEdgesForVertex(vert).Count <= 1); + + internal List GetAssignOrder() + { + List assignOrder = new List(); + assignOrder.AddRange(assignFirst); + + if (vertexes.Count == 0) + { + assignOrder.AddRange(assignLast); + this.assignOrder = assignOrder; + return this.assignOrder; + } + + var s = vertexes.First(); + + if (!EnsureThatEveryVertexHasOneOrZeroInEdge()) throw new Exception("Invalid assign graph!"); + + var cycles = this.findAllUniqueElementaryCycles(); + + foreach (var cycle in cycles) + { + SymbolNode cut_place = cycle[0]; + Edge edge_to_cut = GetInEdgesForVertex(cut_place).First(); + + SymbolNode temp_assign_from = edge_to_cut.from; + SymbolNode temp_vertex = new TempSymbolNode(new TempSymbol()); + + assignOrder.Add(new Edge(temp_assign_from, temp_vertex)); + assignLast.Add(new Edge(temp_vertex, cut_place)); + + edges.Remove(edge_to_cut); + AdjStructuresNeedSync = true; + } + + List roots = vertexes.ToList().FindAll(vertex => GetInEdgesForVertex(vertex).Count == 0); + + void treeTraversal(SymbolNode root) + { + foreach (SymbolNode node in OutAdjStructure[root]) treeTraversal(node); + + var inEdges = GetInEdgesForVertex(root); + if (inEdges.Count() > 0) assignOrder.Add(inEdges.First()); + } + + foreach (SymbolNode root in roots) treeTraversal(root); + + assignOrder.AddRange(assignLast); + this.assignOrder = assignOrder; + return this.assignOrder; + } + + public AssignGraph(List edges, List vertexes) : this(edges) + { + foreach (SymbolNode vert in vertexes) this.vertexes.Add(vert); + } + + public AssignGraph() + { + + } + + public AssignGraph(List e) + { + edges = e; + foreach (var edge in edges) + { + vertexes.Add(edge.from); + vertexes.Add(edge.to); + } + } + public IEnumerator GetEnumerator() + { + return ((IEnumerable)vertexes).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)vertexes).GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/Cycle.cs b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/Cycle.cs new file mode 100644 index 000000000..4935ac742 --- /dev/null +++ b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/Cycle.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; + +namespace AssignTupleDesugarAlgorithm +{ + internal class Cycle + { + List cycle; + + + public int Len => cycle.Count; + + public Cycle(List c) + { + cycle = c; + } + + public SymbolNode this[int i] => cycle[i % Len]; + + + public override string ToString() + { + string res = ""; + foreach (var v in cycle) + { + res += v + "->"; + } + + return res + cycle[0]; + } + } +} \ No newline at end of file diff --git a/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/Edge.cs b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/Edge.cs new file mode 100644 index 000000000..c76cc5dc4 --- /dev/null +++ b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/Edge.cs @@ -0,0 +1,30 @@ +using System; + +namespace AssignTupleDesugarAlgorithm +{ + internal class Edge : IEquatable, IComparable + { + public SymbolNode from; + public SymbolNode to; + public int weight; + + public Edge(SymbolNode f, SymbolNode t, int w = 0) + { + if (weight < 0) throw new ArgumentException("weight can't be negative!"); + from = f; + to = t; + weight = w; + } + + public int CompareTo(Edge other) + { + return weight - other.weight; + } + + public bool Equals(Edge other) + { + if (other is null) return false; + return from == other.from && to == other.to; + } + } +} \ No newline at end of file diff --git a/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/GraphUtils.cs b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/GraphUtils.cs new file mode 100644 index 000000000..e633526cd --- /dev/null +++ b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/GraphUtils.cs @@ -0,0 +1,512 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + + +namespace AssignTupleDesugarAlgorithm +{ + internal static class GraphUtils + { + static private void dfs(this AssignGraph graph, + Action OnEnterGraph, + Func OnProcessVertex, + Action OnEnterNode, + Func OnProcessNodeChild, + Action OnLeaveNode, + Action OnLeaveGraph, + Func> vertexTraversalOrderFunc = null, + IEnumerable graphTraversalOrder = null) + { + void dfs(SymbolNode start_node, + Action OnEnter, + Func OnProcessChild, + Action OnLeave + ) + { + OnEnter(graph, start_node); + IEnumerable vertexTraversalOrder; + if (vertexTraversalOrderFunc == null) vertexTraversalOrder = graph.OutAdjStructure[start_node]; + else vertexTraversalOrder = vertexTraversalOrderFunc(graph, start_node); + foreach (SymbolNode current_node in vertexTraversalOrder) + { + if (OnProcessChild(graph, start_node, current_node)) + dfs(current_node, OnEnter, OnProcessChild, OnLeave); + } + + OnLeave(graph, start_node); + } + + + if (graphTraversalOrder == null) graphTraversalOrder = graph.vertexes; + + OnEnterGraph(graph); + foreach (SymbolNode v in graphTraversalOrder) + { + if (OnProcessVertex(graph, v)) + dfs(v, OnEnter: OnEnterNode, OnProcessChild: OnProcessNodeChild, OnLeave: OnLeaveNode); + } + + OnLeaveGraph(graph); + } + + + private class SynonymsGraph { + + public enum Type { SAME_NAME, INDEXER} + + public HashSet left = new HashSet(); + public HashSet right = new HashSet(); + + public Type type; + + public SynonymsGraph(Type type) + { + this.type = type; + } + + public override string ToString() + { + var res = ""; + if (type == Type.INDEXER) + res += "Indexer"; + else + res += "Same_Name"; + res += "graph(left["; + foreach (var i in left) res += i.ToString() + ","; + res += "], right["; + foreach(var i in right) res += i.ToString() + ","; + res += "])"; + return res; + } + + } + + public static AssignGraph createAssignGraph(List left, List right) + { + if (right.Count < left.Count) throw new Exception("AAAA"); + + + List v_left = new List(); + List v_right = new List(); + List left_visited = new List(); + List assign_first = new List(); + List assign_last = new List(); + + void addTemp(int i, bool toFirst) + { + if(toFirst) + { + var temp_symbol = new TempSymbol(); + assign_first.Add(new Edge(new SymbolNode(right[i]), new TempSymbolNode(temp_symbol))); + right[i] = temp_symbol; + } + else + { + var temp_s = new TempSymbol(); + assign_last.Add(new Edge(new TempSymbolNode(temp_s), new SymbolNode(left[i]))); + left[i] = temp_s; + } + } + + //handle expressions and var params + for (int i = 0; i < left.Count(); i++) + { + if (right[i].type == Symbol.Type.EXPR || right[i].type == Symbol.Type.VAR_PARAM) + { + addTemp(i, true); + } + if (left[i].type == Symbol.Type.VAR_PARAM) + { + addTemp(i, false); + } + } + + for (int i = left.Count - 1; i > -1; i--) + { + //delete loops + if (left[i].StructurallyEquals(right[i])) + { + var f = new SymbolNode(right[i]); + var t = new SymbolNode(left[i]); + assign_first.Add(new Edge(f, t)); + left.RemoveAt(i); + right.RemoveAt(i); + } + // if repeated assigns or pointers -> 2n + else if (left_visited.Find(it => it.StructurallyEquals(left[i])) != null + || right[i].type == Symbol.Type.POINTER + || left[i].type == Symbol.Type.POINTER) + { + var res = new AssignGraph(); + res.assignFirst = make2n(left, right); + return res; + } + else + left_visited.Add(left[i]); + } + + + var indexerGraph = new SynonymsGraph(SynonymsGraph.Type.INDEXER); + var nameToGraph = new Dictionary(); + + + + for (int i = 0; i < left.Count; i++) + { + if (left[i].type == Symbol.Type.INDEXER) + indexerGraph.left.Add(i); + else if (left[i].type == Symbol.Type.FROM_OUTER_SCOPE || left[i].type == Symbol.Type.DOT_NODE) + { + var name = left[i].last_name; + if (!nameToGraph.ContainsKey(name)) + nameToGraph.Add(name, new SynonymsGraph(SynonymsGraph.Type.SAME_NAME)); + nameToGraph[name].left.Add(i); + } + if (right[i].type == Symbol.Type.INDEXER) + indexerGraph.right.Add(i); + else if (right[i].type == Symbol.Type.FROM_OUTER_SCOPE || right[i].type == Symbol.Type.DOT_NODE) + { + var name = right[i].last_name; + if (!nameToGraph.ContainsKey(name)) + nameToGraph.Add(name, new SynonymsGraph(SynonymsGraph.Type.SAME_NAME)); + nameToGraph[name].right.Add(i); + } + } + + + var leftSet = new HashSet(left); + var rightSet = new HashSet(right); + var graphs = new List(); + graphs.AddRange(nameToGraph.Values); + graphs.Add(indexerGraph); + var moved_first = new List(); + var moved_last = new LinkedList(); + var movedAssigns = new HashSet(); + + + void moveAssign(int i, bool toFirst ) + { + Edge assign = new Edge(new SymbolNode(right[i]), new SymbolNode(left[i])); + if (toFirst) + moved_first.Add(assign); + else + moved_last.AddFirst(assign); + movedAssigns.Add(i); + } + + void resolveGraph(SynonymsGraph sGraph) + { + + foreach (var i in sGraph.left) + { + var type = right[i].type; + if (type == Symbol.Type.LOCAL) + { + if (!leftSet.Contains(right[i])) + moveAssign(i, false); + } + else if (type == Symbol.Type.FROM_OUTER_SCOPE) + { + if (!(leftSet.Contains(right[i]) || nameToGraph[right[i].last_name].left.Count() != 0)) + moveAssign(i, false); + } + else if (type == Symbol.Type.DOT_NODE) + { + if (nameToGraph[right[i].last_name].left.Count() == 0) + moveAssign(i, false); + } + else if (type == Symbol.Type.INDEXER) + { + if (indexerGraph.left.Count() == 0) + moveAssign(i, false); + } + } + + foreach (var i in sGraph.right) + { + var type = left[i].type; + if (type == Symbol.Type.LOCAL) + { + if (!right.Contains(left[i])) + moveAssign(i, true); + } + else if (type == Symbol.Type.FROM_OUTER_SCOPE) + { + if (!(rightSet.Contains(left[i]) || nameToGraph[left[i].last_name].right.Count() != 0)) + moveAssign(i, true); + } + else if (type == Symbol.Type.DOT_NODE) + { + if (nameToGraph[left[i].last_name].right.Count() == 0) + moveAssign(i, true); + } + else if (type == Symbol.Type.INDEXER) + { + if (indexerGraph.right.Count() == 0) + moveAssign(i, true); + } + } + foreach(int i in movedAssigns) + { + sGraph.left.Remove(i); + sGraph.right.Remove(i); + } + + var l_size = sGraph.left.Count; + var r_size = sGraph.right.Count; + if (r_size == 0 || l_size == 0) + return; + + Edge movedFromThisGraph = null; + if (l_size < r_size) + { + foreach (var i in sGraph.left) + { + if (sGraph.right.Contains(i)) + { + if (movedFromThisGraph == null) + { + movedFromThisGraph = new Edge(new SymbolNode(right[i]), new SymbolNode(left[i])); + movedAssigns.Add(i); + } + else + addTemp(i, false); + + } + else + addTemp(i, false); + } + if (movedFromThisGraph != null) + moved_last.AddFirst(movedFromThisGraph); + sGraph.left.Clear(); + } + else + { + foreach (var i in sGraph.right) + { + if (sGraph.left.Contains(i)) + { + + if (movedFromThisGraph == null) + { + movedFromThisGraph = new Edge(new SymbolNode(right[i]), new SymbolNode(left[i])); + movedAssigns.Add(i); + } + else + addTemp(i, true); + } + else + addTemp(i, true); + + } + if (movedFromThisGraph != null) + moved_first.Add(movedFromThisGraph); + sGraph.right.Clear(); + } + } + + + + foreach (var g in graphs) + resolveGraph(g); + + for (int i = left.Count - 1; i >= 0; i--) + { + if (movedAssigns.Contains(i)) + { + left.RemoveAt(i); + right.RemoveAt(i); + movedAssigns.Remove(i); + } + } + + + var symbols = new Dictionary(); + + foreach (Symbol sym in left) + { + if (!symbols.ContainsKey(sym.name)) symbols[sym.name] = new SymbolNode(sym); + v_left.Add(symbols[sym.name]); + } + + foreach (Symbol sym in right) + { + if (!symbols.ContainsKey(sym.name)) symbols[sym.name] = new SymbolNode(sym); + v_right.Add(symbols[sym.name]); + } + + List assigns = new List(); + + for (int i = 0; i < v_left.Count; i++) assigns.Add(new Edge(v_right[i], v_left[i], i)); + + + assign_first.AddRange(moved_first); + foreach(var assign in assign_last) + moved_last.AddLast(assign); + + assign_last = moved_last.ToList(); + + var graph = new AssignGraph(assigns); + graph.assignFirst = assign_first; + graph.assignLast = assign_last; + return graph; + } + + + + + private static List make2n(List left, List right) + { + var temps = new List(); + var res = new List(); + + for(int i = 0; i < right.Count; i++) + { + var to = new SymbolNode(new TempSymbol()); + var from = new SymbolNode(right[i]); + temps.Add(to); + res.Add(new Edge(from, to)); + } + + + for (int i = 0; i < left.Count; i++) + { + var to = new SymbolNode(left[i]); + res.Add(new Edge(temps[i], to)); + } + return res; + } + + private static AssignGraph createReversedGraph(this AssignGraph g) + { + List newEdges = new List(); + foreach (var edge in g.edges) + { + newEdges.Add(new Edge(f: edge.to, t: edge.from)); + } + + return new AssignGraph(newEdges, g.vertexes.ToList()); + } + + public static List> findStronglyConnectedComponets(this AssignGraph g) + { + List> res = new List>(); + + Stack route = new Stack(); + HashSet visited = new HashSet(); + + Action onEnterGraph = (_) => + { + route.Clear(); + visited.Clear(); + }; + + Action onEnterNode = (graph, node) => { visited.Add(node); }; + + Action onLeaveNode = (graph, node) => { route.Push(node); }; + + Func onProcessChildNode = (graph, parent, childNode) => + { + if (!visited.Contains(childNode)) + { + return true; + } + else return false; + }; + + Func onProcessVertex = (graph, vert) => { return !visited.Contains(vert); }; + + g.dfs(OnEnterGraph: onEnterGraph, OnProcessVertex: onProcessVertex, + OnEnterNode: onEnterNode, OnProcessNodeChild: onProcessChildNode, + OnLeaveNode: onLeaveNode, + OnLeaveGraph: (_) => { } + ); + + Action onEnterGraphReversed = (_) => { visited.Clear(); }; + + Action onEnterNodeReversed = (graph, node) => + { + visited.Add(node); + res.Last().Add(node); + }; + + Action onLeaveNodeReversed = (graph, node) => { }; + + Func onProcessChildNodeReversed = (graph, parent, childNode) => + { + if (!visited.Contains(childNode)) + { + return true; + } + else return false; + }; + + Func onProcessVertexReversed = (graph, vert) => + { + if (visited.Contains(vert)) return false; + else + { + res.Add(new List()); + return true; + } + }; + + var graphTraversalOrder = route.ToList(); + + var g_rev = g.createReversedGraph(); + + g_rev.dfs(OnEnterGraph: onEnterGraphReversed, OnProcessVertex: onProcessVertexReversed, + OnEnterNode: onEnterNodeReversed, OnProcessNodeChild: onProcessChildNodeReversed, + OnLeaveNode: onLeaveNodeReversed, + OnLeaveGraph: (_) => { }, + graphTraversalOrder: graphTraversalOrder); + + return res; + } + + public static List findAllUniqueElementaryCycles(this AssignGraph g) + { + var res = new List(); + var components = g.findStronglyConnectedComponets(); + foreach (var component in components) + { + if (component.Count() > 1) res.Add(new Cycle(component)); + } + return res; + } + + public static void LogGraph(this AssignGraph graph) + { + foreach (var vert in graph.vertexes) + { + Console.Write("{0}->[", vert); + graph.OutAdjStructure[vert].ForEach(v => Console.Write("{0}, ", v)); + Console.WriteLine("]"); + } + } + + public static void drawGraph(this AssignGraph g) + { + var writer = File.CreateText("graph.dot"); + writer.WriteLine("digraph _graph {"); + + Action enterGraph = (graph) => { graph.resetVertexesColor(); }; + + Func ProcessNode = (graph, node) => node.color != SymbolNode.Color.GREY; + + Action EnterNode = (graph, vert) => { vert.color = SymbolNode.Color.GREY; }; + + Func ProcessNodeChild = (graph, parent, child) => + { + writer.WriteLine(parent.label + "->" + child.label); + return child.color == SymbolNode.Color.WHITE; + }; + + g.dfs(OnEnterGraph: enterGraph, OnEnterNode: EnterNode, OnProcessNodeChild: ProcessNodeChild, + OnProcessVertex: ProcessNode, OnLeaveGraph: (graph) => { graph.resetVertexesColor(); }, OnLeaveNode: (graph, vertex) => { }); + writer.WriteLine("}"); + writer.Close(); + } + } +} \ No newline at end of file diff --git a/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/Symbol.cs b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/Symbol.cs new file mode 100644 index 000000000..2ab9bdfb2 --- /dev/null +++ b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/Symbol.cs @@ -0,0 +1,129 @@ +using System.Collections.Generic; +using System.Diagnostics; +using PascalABCCompiler.SyntaxTree; + +namespace AssignTupleDesugarAlgorithm +{ + public class Symbol + { + public enum Type + { + VAR_PARAM, FROM_OUTER_SCOPE, INDEXER, DOT_NODE, LOCAL, EXPR, POINTER + } + public string name; + public string last_name; + + //public bool canBeSynonym = false; + //public bool isIndexer = false; + //public bool isExpr = false; + public expression node; + public Type type; + + public Symbol(expression ex, BindCollectLightSymInfo binder) + { + node = ex; + if (node is indexer ind) + { + name = ind.ToString(); + last_name = name; + type = Type.INDEXER; + } + else if (node is dot_node dn) + { + var last = dn.right; + while (last is dot_node dn1) + last = dn1.right; + + name = node.ToString(); + last_name = last.ToString(); + type = Type.DOT_NODE; + if (last is indexer) + type = Type.INDEXER; + } + else if (node is ident id) + { + name = id.name; + last_name = name; + if (binder != null) + { + var info = binder.bind(id); + if (isVarParam(info)) + { + type = Type.VAR_PARAM; + } + else if (isFromOuterScope(info)) + { + type = Type.FROM_OUTER_SCOPE; + } + else + { + type = Type.LOCAL; + } + } + } + + else if (node is get_address || node is roof_dereference) + { + type = Type.POINTER; + } + else + { + type = Type.EXPR; + name = node.ToString(); + last_name = name; + } + } + + public bool StructurallyEquals(Symbol sym) => node.ToString() == sym.node.ToString(); + + public override bool Equals(object obj) + { + if (obj is null) return false; + if (obj.GetType() != GetType()) return false; + return name == (obj as Symbol).name; + } + + public override int GetHashCode() + { + return 363513814 + EqualityComparer.Default.GetHashCode(name); + } + + + static bool isFromOuterScope(BindResult bindRes) + { + var symbol = bindRes.symInfo; + + if (symbol == null) + return true; + + if (symbol.SK == SymKind.var && symbol.Attr.HasFlag(SymbolAttributes.varparam_attr)) + return true; + + if (bindRes.path[bindRes.path.Count - 1] is GlobalScopeSyntax) + return true; + + for (var i = 0; i < bindRes.path.Count - 1; i++) + if (bindRes.path[i] is NamedScopeSyntax) + return true; + + return false; + } + + static bool isVarParam(BindResult res) + { + if (res == null) + return true; + return res.symInfo.Attr.HasFlag(SymbolAttributes.varparam_attr); + } + } + + public class TempSymbol : Symbol + { + private static int counter = 0; + private static string getTempPrefix => "" + counter++; + public TempSymbol() : base(new ident(getTempPrefix), null) + { + type = Type.LOCAL; + } + } +} \ No newline at end of file diff --git a/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/SymbolNode.cs b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/SymbolNode.cs new file mode 100644 index 000000000..e6a34c5f0 --- /dev/null +++ b/SyntaxVisitors/SugarVisitors/AssignTuplesDesugarVisitor/desugarAlgoritm/SymbolNode.cs @@ -0,0 +1,67 @@ +using System; + +namespace AssignTupleDesugarAlgorithm +{ + + internal class SymbolNode : IEquatable + { + public readonly string label; + public int? number = null; + public readonly Symbol symbol; + + public SymbolNode(Symbol s) + { + symbol = s; + label = s.name; + } + + public enum Color + { + WHITE, + GREY, + BLACK + } + + public Color color = Color.WHITE; + public void resetColor() => color = Color.WHITE; + + public bool Equals(SymbolNode other) + { + if (other is null) return false; + + return label == other.label; + } + + public override int GetHashCode() + { + return label.GetHashCode(); + } + + public override bool Equals(object other) + { + if (GetType() != other.GetType()) return false; + return Equals((SymbolNode)other); + } + + + public override string ToString() + { + string n = ""; + if (number != null) + { + n = "(" + number.ToString() + ")"; + } + + return label + n; + } + } + + internal class TempSymbolNode : SymbolNode + { + public TempSymbolNode(TempSymbol s) : base(s) + { + + } + } + +} \ No newline at end of file diff --git a/SyntaxVisitors/SugarVisitors/TupleVisitor.cs b/SyntaxVisitors/SugarVisitors/TupleVisitor.cs index 07276ad3c..3e9fdfee4 100644 --- a/SyntaxVisitors/SugarVisitors/TupleVisitor.cs +++ b/SyntaxVisitors/SugarVisitors/TupleVisitor.cs @@ -14,11 +14,14 @@ namespace SyntaxVisitors.SugarVisitors /// public class TupleVisitor : BaseChangeVisitor { - public static TupleVisitor New + public bool optimize_tuple_assign; + + public TupleVisitor(bool optimize_tup_opt) { - get { return new TupleVisitor(); } + optimize_tuple_assign = optimize_tup_opt; } + public static TupleVisitor Create(bool optimize_tup_opt) => new TupleVisitor(optimize_tup_opt); private int num = 0; public string UniqueName() { @@ -85,54 +88,57 @@ namespace SyntaxVisitors.SugarVisitors public override void visit(assign_tuple asstup) { - if (asstup.expr is tuple_node tn && tn.el.expressions.All(ex => ex is const_node) && !tn.el.expressions.Any(ex => ex is nil_const)) + if (!optimize_tuple_assign) { - var n = asstup.vars.variables.Count(); - if (n > tn.el.Count) - throw new SyntaxVisitorError("TOO_MANY_ELEMENTS_ON_LEFT_SIDE_OF_TUPLE_ASSIGNMENT", asstup.vars.variables[0]); - - // Оптимизация, т.к. все - константы - var sl = new List(); - for (var i = 0; i < n; i++) + if (asstup.expr is tuple_node tn && tn.el.expressions.All(ex => ex is const_node) && !tn.el.expressions.Any(ex => ex is nil_const)) { - var a = new assign(asstup.vars.variables[i], - tn.el.expressions[i], - Operators.Assignment, - asstup.vars.variables[i].source_context); - sl.Add(a); - } - ReplaceStatementUsingParent(asstup, sl); - } - else if (asstup.expr is tuple_node tn1 && !tn1.el.expressions.Any(ex => ex is nil_const)) - { - var n = asstup.vars.variables.Count(); - if (n > tn1.el.Count) - throw new SyntaxVisitorError("TOO_MANY_ELEMENTS_ON_LEFT_SIDE_OF_TUPLE_ASSIGNMENT", asstup.vars.variables[0]); + var n = asstup.vars.variables.Count(); + if (n > tn.el.Count) + throw new SyntaxVisitorError("TOO_MANY_ELEMENTS_ON_LEFT_SIDE_OF_TUPLE_ASSIGNMENT", asstup.vars.variables[0]); - var sl = new List(); - for (var i = 0; i < n; i++) + // Оптимизация, т.к. все - константы + var sl = new List(); + for (var i = 0; i < n; i++) + { + var a = new assign(asstup.vars.variables[i], + tn.el.expressions[i], + Operators.Assignment, + asstup.vars.variables[i].source_context); + sl.Add(a); + } + ReplaceStatementUsingParent(asstup, sl); + } + else if (asstup.expr is tuple_node tn1 && !tn1.el.expressions.Any(ex => ex is nil_const)) { - var temp_id = new ident(UniqueName(), tn1.el.expressions[i].source_context); - var var_def = new var_def_statement(temp_id, tn1.el.expressions[i], tn1.el.expressions[i].source_context); - var a = new var_statement(var_def, tn1.el.expressions[i].source_context); - sl.Add(a); - } + var n = asstup.vars.variables.Count(); + if (n > tn1.el.Count) + throw new SyntaxVisitorError("TOO_MANY_ELEMENTS_ON_LEFT_SIDE_OF_TUPLE_ASSIGNMENT", asstup.vars.variables[0]); - //for (var i = 0; i < n; i++) - for (var i = n-1; i >= 0; i--) + var sl = new List(); + for (var i = 0; i < n; i++) + { + var temp_id = new ident(UniqueName(), tn1.el.expressions[i].source_context); + var var_def = new var_def_statement(temp_id, tn1.el.expressions[i], tn1.el.expressions[i].source_context); + var a = new var_statement(var_def, tn1.el.expressions[i].source_context); + sl.Add(a); + } + + //for (var i = 0; i < n; i++) + for (var i = n - 1; i >= 0; i--) + { + var a = new assign(asstup.vars.variables[i], + (sl[i] as var_statement).var_def.vars.idents[0], + Operators.Assignment, + asstup.vars.variables[i].source_context); + sl.Add(a); + } + + ReplaceStatementUsingParent(asstup, sl); + } + else { - var a = new assign(asstup.vars.variables[i], - (sl[i] as var_statement).var_def.vars.idents[0], - Operators.Assignment, - asstup.vars.variables[i].source_context); - sl.Add(a); + DefaultVisit(asstup); } - - ReplaceStatementUsingParent(asstup, sl); - } - else - { - DefaultVisit(asstup); } } diff --git a/SyntaxVisitors/SyntaxVisitors.csproj b/SyntaxVisitors/SyntaxVisitors.csproj index c241e422c..1bc6a61ad 100644 --- a/SyntaxVisitors/SyntaxVisitors.csproj +++ b/SyntaxVisitors/SyntaxVisitors.csproj @@ -52,6 +52,14 @@ + + + + + + + + @@ -61,11 +69,12 @@ + - + diff --git a/bin/AssignTupleOptimizer.dll b/bin/AssignTupleOptimizer.dll new file mode 100644 index 0000000000000000000000000000000000000000..34f74139422fae995fdc16a97b32dfded19d0c18 GIT binary patch literal 23040 zcmeHv4|rTvweLEA=FFU#WHM(aOiuvDkx7->=mwmP~l!h{XV>+$hAJ@Ud5|d6z)~g_t1w5T>m^Sa@8mID&hUs z+GqYGRKfeb?|bk2o&&SaUVE**_F8MNz4o6u=_Oa(P6iQ~_`dQA(dY2w&qjfN987~8 zKkIMf^q~FNlFw;mMYm}b*w6udt3DesTi8BNp!Z7K_yN zNvLNw?w#TX*uW9>Vho^vv?kk;)HAgx)sj6CN9&a7m}s90Iic2+J_+Hd@IZ-rbpXu- z{sanbU;SZSQlDu+&@A*ERM1moc}eC=F{lSfS=m)dD>ILaVRFK%gNPI14s6FRcMzG? zft6`Mi?*+z18b%Mc9lEOjZ7m*)S@k7A9m6ChY4D7`Mq?(ld7ER(5YSO;1_>B_@miI zvW&?ED0*jbd&66ZM?xb*T-Ej?t0qg4maBCia-&mhwB8^d(=%ssgO)RV_{?fqrB7lC zo0E%C*>ac^*3@P+XTyef7IQ+yw6p-#$Sjd6X;b1_Dpcsldir=mAoK*ZqL$$;W3nZK z)-a>T!mWEq_u+nPiaS;vS~4s=fL6eoSzhU=O2w%rR4Y7?44tG0YN3T4CjD5V;`H(J zHPFa!hJ+e{wXcevl(qJ&;K2}c1f6z>yWS24(w#^KfoG+nPL#(!=EPdN$x*{zfV;+cgCett2+cAPW^iDyYkTFDS5)Zz(b%q8`w6zaMum4RIkhUh<4&AStaIvGeHb2B zh9{f^*E){Vx`LdzSfdP2`~kzoz<_a%f%YsL<`_7eILp8qnON(#Dg$d~&8>*d)>|pl zZ-Wz3A;PSOx1*`#+P`2slNqF{XevccY7%}iC!mv_s(0$yta;A7*0to+Ph#queg_!A z6+*dEQSYzh_6<(RY4BH}=&c68FrHVESmAwDW5eqNvyWkorWzrkF=)(ctwH6wE0syf znFdUiQo7I$C)AiZSGqw6X$UGEE6aaNYC+EE)V}zuQhAYn|D(FpFT}jO7bZi0hw7tx z1_djYJwg;#DFllX66Y2q!Y-7MI4c2a$iQaSp}uu~P|vC>1HAK4%xVpsV_QRHN3&5* zr|VJM_9>N4p+QOguq3G%DFs?Bq;-uOBQviXhrJfkD>+t+hH=yB4C+6m^56FAe33aH z!l{LAg7n#s!P&B4bVyQF3D#2UcM%L$Opl!LrY+`rYjkP12GQVfO}ikq3u%uLx> zVYE!i^l)7Z43qTC%IcY3se6;2*&+>eV~WF4H>WUMChMER`UvxQZ{Tn)Mq)vR^{@(9 zvG6fSGMm~On~z~7FlQ_{#PTnMq^4=6WHhLdK?2Om!XUXCIujNn$eKjDGg~A2Bv3P} zHNui5SZOm@{j4dbp^RB+O(7V0Az`+lRV+(kD93B}U62P$k^3(~fgJ=-n%>1gV%d2~ zEz^fmjTP}P!PDCgK&ae-XWNevEZR&1CU+@V&>D7{-cFEfv&)i3=2DbWrsbHEh!4}l z1}};-M&I=MQ6{hq;Mw+V$TDUcuuhg_S*Z{LE5trj>ufzOT~k_TcTvY%D(h^OPuSK! z-6!1Y|AtS*4}nj_KmVtEVo($Oh6&>M2tHoYr?7*-!p*~D%~%^b(00S|-W~wiJVxNlcq&LC1!q#ai&=@gU;2Q zj?e6lMAQ!OTYkEDBf^ zhEYhxvdLsD<8raaa%#L@@DXYbpr-9vj7e;!0lqE?E%}di4E(36rlP3PCmE({F+FR8 zJ#dch{7q?YX0@{}QtKJ#dkhg19Hc(vIH(DUtWp%og+wDHeoi@IZv@rNs1&6*xgb!g zlxhj$#G$3DRbMRs(~yX%ww_Ou`Fh!;Fb#FIM;nPvJx8`+BUvoJp|) z^C0MVz(9mmyF# zLcy|uycD8$qJ0C}CrO_|o-mUX4^J&c0qF{-O%3O$T5FOiGPa3`p)fBG&6!6=yC&Nb z@jTSjaw21H+ZzWe`?a`@-AO!zsA`kh6!A8K)b`CE#v-lZNU%?eTh($)adrW%nNWKs z&Y>%ixsa-`?q31{QGqf4T9j*auYgD6<*8^kM-d%IA0N0^1ZZn4p+>2C0@c`k$Z2X3 zjv7%9;e*3%so+E#_okwht*c174h+o%0=jUb^Jer^jZ@Qhr_9V+fhkiz)(p#KZ`6ds zjMWEw{Ud1EG|iL@0zaDy8OP3^%?4{qY)nyXRs*utZs=4irc7dx%@mGEt?N{XOTJFk z*(C;lHKaIolEJID zT4{~YR;v!qHYUzCy!Mnti#63|fZ#L)W~@Dd-ux|KPc@Q*;Z^ELn<=bUW~pbXNzG@5 zV1R>ylfpQr&}-w2bs>9yH#GL908$H_1$;0&!#SfB+dgN3TGvpT?=()b@M^7~2Sn|K zsFaF8_-moFaNYN3a8J3mO?hq6THKf5?BFb_^4gisnO*;eS~B#YKJZ!zEqiUTv$*xb zO6!W(&cfI$k!tMEI?dQGa?UC_?M8IwZ)K;Y$w`;GQBGSfPOB1@TEYy0(}IGt2u@pM zJLwr~+iagLb(XTvmO0B>ZQEI@TxL5G|NfXJB|w83Z3no-P%yDcACW&?=7HWCX~5hcT{cM3Bqmy z)^J+?S4(xa+&R#f#Eu5efPn5p!bnbo7p z8IgumHrwPx!cJC}7b|!=*{Y3~lZC^wPS%n|5h2#!W%eyss?Vxe$AYc zEt;Kb!$zkKOGt1I`MGgLkBJ45&%67~D?Y2roKvw@6Bc;nRNjg5y2;xIISr|G^HNmv zx+%+t+B41?Pb{qi?9*V~tlBJK-9*AOWuNDt0ab&g@-%CuP1`U<7C;Z)Ia?S%|7HlC zLQXR+AJaWLg@q2qgIrw2#Tjca2jN>$5d#A4E&pvmYJgz-tdLqDZ)D^aP;jQ3v9M>h z{973d>0u`naZLMI2J))1_I74rxlPkdsjtrrt#=;I;QBoGLH!;RtrozWo%oeeu zL~5F5N-6^OL9P=$Y+zS0CVF7E&9;e~QrPIDPm329$KtIv1~qaF16i%$PHw9dWJrj!eXJOTy>|eu$u{E_R}tVRc6YH@FINd4YS)~u`0D? z%CU~wB##Yx?aFc~QCvPJNb2f+AKUV8g8PlD# z=7icGUXNo_J(R=)qmNrIY!46^*jlH6w#Z((8}^+k_Eq-Es|@chaOxR`nY#gMBC?5I zpiAmE_?ID7l*`G|-kI+n;48H^Pk;BT_s`4V;DMhExI6ECDDIU_dI~u<9q<*F!S}}+ zz|hpVcbcD31n&E}W5iIj4dqcpbDxKAoNtq8qI!+tQsIU@l3hZoPJLQ*+!a1l7 z&T2{@OZ+6)j4e1wt`>iBZ1DZ+1$dq%eRQE+)W3(NTRuV@1MGbmkJ_+hH`IF{K@oTN zakSsa)!~>=#*)tquq(}WVNjSSgA#uHLOP1d9YrjgC>?h7 z2w(N$&O}q!6aVt6lWlc3QHy_X$>I=DYquu#NpxbWyCQY_1IMZyzktpR?ljAWTvW0U z5oUTk*cBtoG~Nj+TDyNSSf-m$QQ=!`$d0$A^h8cl|F0=rh7mN>D$&x6of?ACqLwx0 zBu9VajK>GA!>|e`P@yopyqjoIi~LRRX1jtm+wT@&F=if?9O#r!ZR_9L~z>- zzq8CgrGI47YF$;i&chsFR8B2O+mM)Ykw$1w|* z^%o1H`4OKvdJrnZ8tQiU(?<~{Ak(r7cW>*#^K-yo!cJw`rZEo-rhIgS=4EGnIA$Y& z=|8m9e4&Bnl4meA<(&xT9l|$+FRYgL9+;NK*TfgL%9qJI9JSTn>>Z_afhie2cCTSRdwo4@>V?iiD>{ zpL0adFQWIw)D?Tab}_YB&%@HUq7{!{7l#1Gmq#-}zlvn*&J;`ocDN>6w+-(Va6}}i z5m*l88qgEefL{oXB7TtEf$g>#!@C)RMZ%2L$t+FKcP+*eSZ}A1GHK5!On0XX>Z5Hfo&^|>=Xp3N;a4rk5b(ls+X*u4qsr1l0Xj`6bG##*F?N<&%KTG*x=u*Iw0&j~yZ#C1Za5HMU!z|(P8l0f% z_88aSZgc&|qbtJA^rX%G{$p%qcnu{&=Y$*RCh7w9p*ohP;L+M1l$8!gY%bpx+6t)p zV#sYTN83KS60lKcc(FMG__R3=_^ig|XK51f72@&^{U*Tk45pgGGYNjow#e;(-w3?~ zt*)%S3+3an`@)uWW$1Zp&-A9y%J81)y#lua?o7k&^eEjOe+bcXa0z3_={>dghjsd} zU?=8BYw#`(eL=93k%#L(6E^6}iZc?bdk(ZG1bdWjjlBTug#f!J{sJO9v9#2~?-1Fy zm$8R1mYjKsPS7Lv--g3^W^JdP!sv&3}XH{l?n<$eFZPusdj?_VUPLGCP=d zl6KhlN6x0b*lQ8pK~47OBP-}>!A{b}@xP0-(Askq=LNM-M6&c?fNim#inP+swThMp z)<(meV(8t-CO?j})ADtU@%SZZ74-=ADBW254s$i#8DNX8M~zN8CD7^d*eS8tM`3Iqk;U_Q)D43ic?m%ya3^0OS77rEdfn`)n;OU0;%! zve#0FU?*vLG_H5iWSOQRW8Ng#9rF`4OuKy+?JmJ?q}ynL-9?|RqOGGZ2DAsX{>VD| zRu%0$`d&c$I%wz7PpfF_>BWF{7%{P)A~-4#-G~@wMC}1q3z>biGQe&^ zf7|Ii!IZT-s3)Mk9Wr;&V1Qk%ud;U%ze>Yi%0uQZnhvn<8Jp|@`b2=8Wv;U?qsId5 zBzm}~qQ)ShQ;(WEV zSGS#k{swi5p4HO+P}@Jmui7f=zhf`T|J{s}1>U{H?!E@Mk~VG|`1kQ>O$a%k%S!;G z*za+6{dcLcvC7P&6&lxUqbFZDNJak`e;!zYlX{~>fc zYTi1)7DdT3q2Ws z4$@ewE`g7bUH1!mSmT|-BLU=n!owOvmh(IMns#nj(;lHE;M^yzUX&H>3C)l1(U^Lr zcAOpw?br4}&k>F54{5`~IVQRl16$bMPoe&8+K*uSyTQZ!chS9dcWDo!{)1Xk^zWh{ z$M4q$r0osT_6cnu@z2`#wbk+OYc~kb9U}iG@VubCLwFpG{qr^LdFZpAK397?_Ncr+ zVGZN@Bf|fpz&iok^cS_`@zsDA)%EM|llprh?Ueork@ld}ybl_F2Q@7Dl<4zip}voP zVgEvZQ1t(@P`@EEPti&H75&?R{JZTH#wn3=O8oz#c0-+SXgd4hDUtS!NINBRPSH0b zx6?Bs?SG1d2SwV0!uf0A{IyW;1^?~Xq4PM_fck=Vik!MfjRynx3;XMaExr7nerAnm zrpB>Uqy5aPGwb1b4dclC`(VY_je3oJ^`flz`{?R;i0U<#zX+V{rFnICoBQb3_TQLX zei&uuKSj3nWpjBTjU%>IdReY*P25h)wO`xc(U)uNrB)f2PT^cH@b_X}kI3&6SR?Q$ zv8WTX<60UJX-|n~2FfG*)zCiSe2~txzY}uwx7Veu1^T(+1TE122j)c&Z2XQsESB~G zUSz#USA^eaEzz$^Ls_K0?`c;iK_7bDvVzl~@~YNIEu2xdIPLx4$o zkHEX6=6eF4qNhN8nto)px_D%+xU?23F9H-OUC2I4~!of&l&U0r_EoP?+QH~I@{W9-D17d`m|+W z6|j(V!&tBI;}cfjFVyv64Y2Dj1^i-o58zt`CZh*Xe#yQXa8+X5#=44j{8t|#KnHF4 zofsSNEJ)IkPip~J;_PkVY+MJAEyWx9tLSRFo^GX&(F61t?a+MfdhNLO4ee*zOWHF1 zul3L9kLuslf1p33|5i^MZ#GUC?=tQ-eq+3BnC8vq9p>Za_sxSLKQtM-Idm%YSZJMf zjrA7GoQC4FKJUe9$#(^%&o?a>tJ&PT{cJF-fJw9*I{^NC6sr%fjn$v!SeNFuRd*KE zT(~(xuXE*nLgd!LO9^0^7+(CW%yJ<-O6s z&H%=*=#%De<-6YaE!}49*1p2Ju0JrEAI2q}mme8By4lO;-Jzm8yxBW6?&aO0zhVF2 z;K~kKzhP)_uxHdCAIlxxJeKqQjzLroAMiJf3=ZOPgWwywz|4f9f}1P4-M&9MlE1K! z8$T#wm@-$6uyecBOdWBHs?mu-jQi~3x(C3Ym^(j-VOE3aWeVOi^}B6uI|Tk(7g1u6#XxtR7@OHIyj)fuUrUIU%uf zFle`i3v>L5n%6dOC2gM=8_VqPoa zznB}k2G8!H;;5HLp%;u}qeVnE3v&xBI1p9z7{k~d?01VO^^N-cm^(HI@M6Jr2MRe1 zxSt!l0TS}X)vJ_`)(oOB$5zyw)@!JLZ6BVa@C5=G>A}YhMrd z>Ort==z2Y@V7kN`o)~jC&}Cb<4fN8czV6KzQ~y!F=pJg{wu3I*)q6P|&W%mDgM)O) zAMy%gqx&I!sKm3`8yiEaMU?vO7rJ@3FgipCFl3W*q)8IwXrS)ltC6B7hKk5N{&v?L z?mvp!9@iftq|Cu>!{8XE!=wHvayN|*(~kVkf;Z&){&xJ$>*j-_V^viO9g@mjZf&*|-7AynM?a;;iyoNz>n2@G;er$#i+L-QBy|IAQ zn=iTrDf>tB#oQ5@MuT~7SDY9hb5+qV<_bks+%y3_2Qb&rmp_yn>)y1Pr|syNTWE*D z5Fy0ZnS#khCShmmOt!WgGVKg&^BJsJfG7DZ9X~#I3#cjdL%4`zK18)IxJ#? zqOmwTpF8BzWd(S;Z#3`Hfr57kY1b|E4|(G*?cpIGIEoT{C(j@zHhTqE4OYH-G)ZNO zZL=#!XQMpE=;Ji)=GJ<1xHgY@zKaaPb_QC*GUZc=I;73v?jnAp+YcKMQTr!GM%W>h zx`A8){cpivRlC=Dg=;E=-Xldf?<0UKRXy&3+{9S%68F%4w@|58J?1#rce!J^BU17! z^j#DA;^-k)a&&QY|0pNy3bg>ElYu~Hn;F87dk*rj3jKxvu< zqT8`jva0wglGF}kfJ3lvbmU+WLAxz~z{99x6q#YbQ$Jb~R@mO$(7|AgcDcL&xWlqb zD0Qij(zYVttlId1Pv(K*DVd-=8LBB!l z(&V{>_k6Bm#H{3Ux(6pzl%ojeDt z_LFR0HJX#DEm@Yl;z5Wj=N(CQRrq%Ix;~_liQI@AkT&J~QnB2sG_%^vHE0*`E43wW zD7Ay)b(~2Gr7)o(4|4&y(o=%8Jo1DAaEpj!EE2Ad1q3h0OO+}PNku8aVRuvVVf9i| zB4dhU*(p}GLexIQP1=X#QPq7gJ@)Y80){|X^~@=e5Oh3xCA3P0kELp2Bv&{Mscc<| z7Id(Cd|a&_eu>b%-!B$&L&Xx#BOr6D_sD3ua@6ldpsLx#g&cAl4Na7N(KDJG$$Nfr zbjUBcr<5W}qu1{i4v%8(46-3I0nHeJXRaY*f{lmsDl}bC*>WoZO0+*TA!Wdw%#*nc(lKh%%hgvXeBph(xboR6s>@Gu7)@z{z1!P0`w4P1Aa)N(WTl!4289M;8xwZ9IJ6GAI2kC;-9AP%pp7dQ=H5I}5Oyi^Bvez8c6s<~ zlLCXPct+65Xa*UwFXtDrYVZMK?$R_k#+8);Qg}`>&z8{wXqRBc+RpzV$7bZUqRSbL z`n`$55Y|Ja0${9?FExWhLDgZY97KAd%W`Abu8jm^$2GDOQ6q|hl{pz;7rMo{heytl z*hS1fHX+DjlE#dALJM15OaU1w2hY-aPPV9GDhc4tNA*yd~F|qhb8lk9Qgl(SC4@ z;eIHMA4x6Qgn^*Ojroma*BrboZ@M`sP@Vc;W9CR`=_^!YUuI+fYTWI84u%Hv`Y#Z>>T z(2nSab(?=Y`pN9JQx6_ZwoWfhkO6O-X-$iS0GxSTjEB;coW8}HmvpscM|fUx`YtV* z=I8r^C-9Di;b?M&KF?XHCC|ZsU8;C~@_ZCc2smG)JQhjQykr}AAdVl5qfLq|)fB8L zRw5pnmt4YKYr5dNH2+Si`T43j9^4^hYg)3YE^LU&XZ@fQ(B`g6&(w^Vt?wFjEEVrM$b!#w5BD1;oq}R-dd-w(7~tlUJ&q@m{|X z$A@Ldx$iFSE5+BDs8u|>)uUb+R3A+nTCxjLr=J(SE*JBkm+Et*I?f!sWlVNE)4xfE z!^!SsuV~W^Mf7C164i}f(2WI0LRcgWvqrTzyVup?;?7Z4MQV+(rpMAIdUg&auXQF2 zM0z4@BNZ5mU+ zg&#?^WPKvcjiztYlN}`yZugxZ%5XfnH85(CGEIfZqU0i&lP;UYrg4C!WAIPOH%h}rO2b7gQ05s_>rizaQC0>X zLGw}(&WVJf_b6&Zux9!e%pab=DT>sD&3K7Fj^T}%aJ(K5hnzkM1eT+-^&irb^U*uY zhyuM2gAPh0t*0>mQrcL!T^Un?aJZTp7#Ox9!k)QJkC&`U&X4GNM8`bf2cF5DY<>wD zP>86ONoJHyTtE}ngT*8F=!*h}XT;$^ID%vh7LzzUV_|Ud@(%t%A}t`t*mCgj0G72< z?#m>@rqZ_*R>}EAjMzS~vj_0_7!+V5Duqv)Tmx^QGVsfv%nlcIsA;4e2p_jesHued z8IXBism^OqDtEdfNM906sxj*xl-T#I7#@qFm3qv1Exsri*N!N%jcA;r3y>zRs%T#^ zr)+k)k;riFn#fUJ{Qx$D7z< z{4hz*@fFUjA!St}(ZD^ChpykZXF=!Fx0qP!$kZ6<0G32pT(|J2%*h148Fn&at|M@E zTDNpg%vC8Tpmcz{L${!q($lbLK<6hIt#$`0_u2suqMS<{aMDcqzEsx{ ztF%>&935O)iWt+v7+_Fk*pc*MU9tx03=2S4M8+Daos(V?Gpe3cc&NoIBW*Huyb4zt zR}zNwR9az?3w6~)TG}AJsyvRl&aEZe@S;*7bYKJ_Ai^sX#(`5%B$)=pI;Fi^C!9VkeZ2XA z3k9~ad+Ws*v=);TVZQho`2kPK9Cq4}XR^bLU|7rywi?;GWiQwJqph0k^`U%vbpI9Twm^EL3{7j1kl)aZ=%?Y#r#i?-I_%IbW6%^o7+ z^<`4;;$tAc=x=cxT;%#5>d&tJ zp8f~ysh@xFrT1KX>$iRvzhdvFA7mXjuiLlV#}(MV+?adhz&>2^jO-hiBV}&?5PmG& zH~WO7uB-Opa%-R5E1s6zK0dskR*qM?znIJE3SM10zlx7&5Lc=_V`FkVB{%G@D|cf- zi&vT;X0B<~(*9ijf64>=-n{(H^7L%A)EoUBM6ar6zODG~1?2_~@ZfWUjz4XoJ=Bl$ z(;n)@KX2KOcdvHPcHr9px2S)0lfGcSFvG{bxgU9<5S6^ZREZU~9zYFJb2vF}!D(&` zCow)_9^lg^KFg)mfSfDyIHQdP=R^-Y>hpm46uQ))6YdotA&)cQt0jF-csg*ZRR4A2 ztP3CDy)hkaOZ+`@4jd9WxLCgfPwx2;zSm11eBQ-PHd+v6MBhHt^ND#3@44`q z{-|iIPS}KLN_2(vy(6e+uP}GXPR9Q8I&_L3c0#rX%_eZ7FJc5|d!yn9_6h%O5&KSA j%xCfa(B>#KeXVwA`sdH<^}u-W@7uD&|NQy?&IA7&;O