Compare commits

...

6 commits

Author SHA1 Message Date
nevermind322 75e79d9a82
Assign tuple optimizer (#3160)
* 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

* fix #3116

* (the rest of the fix)

* potential symomyms handling without assigns count minimization

* assign count minimization

* rename AssignTuplesDesugar

* move NewAssignTupleDesugar

* add optimization switch

* delete AssignTupleDesugar project

* remove comments

* collect symbols from var tuples

* optimize var_tuple_def_statement and assign_var_tuple

* fix

* fix Tuple Visitor

* #3091

* #3125

* revert #3125

* #3125

* #3123

* #3123

* #3127

* fix

* del doc

* await переместил

* Перегенерировал грамматику

* s.ToLines
RenameDirectory

* Исправление ToLines

* Убрал из грамматики узлы await - случайно оставались

* Sync `.ToLines` with `System.IO.StringReader` (#3154)

* 3.9.0.3494

* add const optimization

* #3152

* #3152

---------

Co-authored-by: Sun Serega <sunserega2@gmail.com>
Co-authored-by: Ivan Bondarev <ibond84@googlemail.com>
Co-authored-by: Mikhalkovich Stanislav <miks@math.sfedu.ru>
Co-authored-by: Артём Филонов <afilono@sfedu.ru>
2024-06-24 19:54:40 +03:00
nevermind322 66eaa619a3
Assign tuple optimizer (#3158)
* 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

* collect symbols from var tuples

* optimize var_tuple_def_statement and assign_var_tuple

* fix

* fix Tuple Visitor

* fix

* add const optimization
2024-06-23 13:05:10 +03:00
nevermind322 d269c7ca20
Assign tuple optimizer (#3147)
* 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

* collect symbols from var tuples

* optimize var_tuple_def_statement and assign_var_tuple

* fix

* fix Tuple Visitor

* fix
2024-06-12 16:15:43 +03:00
Mikhalkovich Stanislav 54f8db8ce2 3.9.0.3489 2024-06-08 20:54:30 +03:00
nevermind322 2dc3c4e50c
fix CollectLightSymInfo (#3143)
* 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

* collect symbols from var tuples

* optimize var_tuple_def_statement and assign_var_tuple

* fix

* fix Tuple Visitor
2024-06-07 20:57:03 +03:00
nevermind322 133d082e4e
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
2024-06-06 23:10:38 +03:00
43 changed files with 3797 additions and 2461 deletions

View file

@ -2709,16 +2709,19 @@ namespace PascalABCCompiler
if (!PCUFileNamesDictionary.TryGetValue(cacheKey, out var fileNameWithPriority))
{
if (FindFileWithExtensionInDirs(fileName, out _, currentPath) is string resultFileName1)
if (Path.GetExtension(fileName) != CompilerOptions.CompiledUnitExtension)
fileNameWithPriority = null;
else if (FindFileWithExtensionInDirs(fileName, out _, currentPath) is string resultFileName1)
fileNameWithPriority = Tuple.Create(resultFileName1, 1);
else if (FindFileWithExtensionInDirs(Path.GetFileName(fileName), out _, CompilerOptions.OutputDirectory) is string resultFileName2)
else if (CompilerOptions.OutputDirectory != CompilerOptions.SourceFileDirectory && FindFileWithExtensionInDirs(Path.GetFileName(fileName), out _, CompilerOptions.OutputDirectory) is string resultFileName2)
fileNameWithPriority = Tuple.Create(resultFileName2, 2);
else if (FindFileWithExtensionInDirs(fileName, out var dirIndex, CompilerOptions.SearchDirectories.ToArray()) is string resultFileName3)
fileNameWithPriority = Tuple.Create(resultFileName3, 3 + dirIndex);
else
fileNameWithPriority = null;
PCUFileNamesDictionary[cacheKey] = fileNameWithPriority;
PCUFileNamesDictionary[cacheKey] = fileNameWithPriority;
}
folderPriority = fileNameWithPriority?.Item2 ?? 0;
@ -2730,8 +2733,9 @@ namespace PascalABCCompiler
var cacheKey = Tuple.Create(fileName.ToLower(), currentPath?.ToLower());
if (!SourceFileNamesDictionary.TryGetValue(cacheKey, out var fileNameWithPriority))
{
if (FindSourceFileNameInDirs(fileName, out _, currentPath) is string resultFileName1)
{
if (FindSourceFileNameInDirs(fileName, out _, currentPath) is string resultFileName1)
fileNameWithPriority = Tuple.Create(resultFileName1, 1);
else if (FindSourceFileNameInDirs(fileName, out var dirIndex, CompilerOptions.SearchDirectories.ToArray()) is string resultFileName2)
fileNameWithPriority = Tuple.Create(resultFileName2, 3 + dirIndex);
@ -3362,8 +3366,18 @@ namespace PascalABCCompiler
// It's not always possible to solve by re-ordering the references, since there are cases of
// mutually-dependent assemblies (i.e. dependency loops) in the wild.
foreach (var reference in referenceDirectives)
PreloadReference(reference);
{
try
{
PreloadReference(reference);
}
catch (FileLoadException ex)
{
throw new CommonCompilerError(ex.Message, compilationUnit.SyntaxTree.file_name, reference.location.begin_line_num, reference.location.end_line_num);
}
}
foreach (var reference in referenceDirectives)
CompileReference(dlls, reference);

View file

@ -1847,7 +1847,10 @@ namespace PascalABCCompiler.PCU
common_method_node raise_meth = null;
if (CanReadObject())
raise_meth = GetClassMethod(br.ReadInt32());
class_field cf = GetClassField(br.ReadInt32());
int field_off = br.ReadInt32();
class_field cf = null;
if (field_off > 0)
cf = GetClassField(field_off);
common_type_node cont = (common_type_node)GetTypeReference(br.ReadInt32());
if (name==null)
name = GetStringInClass(cont, name_ref);

View file

@ -3021,7 +3021,10 @@ namespace PascalABCCompiler.PCU
bw.Write(GetMemberOffset(_event.remove_method));
if (CanWriteObject(_event.raise_method))
bw.Write(GetMemberOffset(_event.raise_method));
bw.Write(GetMemberOffset(_event.field));
if (!_event.cont_type.IsInterface)
bw.Write(GetMemberOffset(_event.field));
else
bw.Write(0);
bw.Write(offset);
bw.Write((byte)_event.field_access_level);
bw.Write((byte)_event.polymorphic_state);

View file

@ -15,7 +15,7 @@ internal static class RevisionClass
public const string Major = "3";
public const string Minor = "9";
public const string Build = "0";
public const string Revision = "3488";
public const string Revision = "3494";
public const string MainVersion = Major + "." + Minor;
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;

View file

@ -1,4 +1,4 @@
%COREVERSION%=0
%REVISION%=3488
%REVISION%=3494
%MINOR%=9
%MAJOR%=3

View file

@ -1,9 +1,9 @@
//
// This CSharp output file generated by Gardens Point LEX
// Version: 1.1.3.301
// Machine: DESKTOP-V3E9T2U
// DateTime: 25.05.2024 22:13:44
// UserName: alex
// Machine: DESKTOP-G8V08V4
// DateTime: 17.06.2024 16:43:45
// UserName: ?????????
// GPLEX input file <ABCPascal.lex>
// GPLEX frame file <embedded resource>
//

View file

@ -3373,8 +3373,6 @@ expr_l1_for_lambda
expr_dq
: relop_expr
{ $$ = $1; }
| tkAwait relop_expr
{ $$ = $2; }
| expr_dq tkDoubleQuestion relop_expr
{ $$ = new double_question_node($1 as expression, $3 as expression, @$);}
;

File diff suppressed because it is too large Load diff

View file

@ -377,6 +377,9 @@ script=

View file

@ -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);

Binary file not shown.

View file

@ -4707,9 +4707,12 @@ namespace PascalABCCompiler.NETGenerator
{
ICommonTypeNode ctn = init_value.type as ICommonTypeNode;
IExpressionNode[] FieldValues = init_value.FieldValues;
ICommonClassFieldNode[] Fields = ctn.fields;
List<ICommonClassFieldNode> Fields = new List<ICommonClassFieldNode>();
foreach (var field in ctn.fields)
if (field.polymorphic_state != polymorphic_state.ps_static)
Fields.Add(field);
for (int i = 0; i < Fields.Length; i++)
for (int i = 0; i < Fields.Count; i++)
{
FldInfo field = helper.GetField(Fields[i]);
if (FieldValues[i] is IArrayInitializer)
@ -4750,9 +4753,12 @@ namespace PascalABCCompiler.NETGenerator
{
ICommonTypeNode ctn = init_value.type as ICommonTypeNode;
IConstantNode[] FieldValues = init_value.FieldValues;
ICommonClassFieldNode[] Fields = ctn.fields;
List<ICommonClassFieldNode> Fields = new List<ICommonClassFieldNode>();
foreach (var field in ctn.fields)
if (field.polymorphic_state != polymorphic_state.ps_static)
Fields.Add(field);
for (int i = 0; i < Fields.Length; i++)
for (int i = 0; i < Fields.Count; i++)
{
FldInfo field = helper.GetField(Fields[i]);
if (FieldValues[i] is IArrayConstantNode)

View file

@ -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

View file

@ -1 +1 @@
3.9.0.3488
3.9.0.3494

View file

@ -1 +1 @@
!define VERSION '3.9.0.3488'
!define VERSION '3.9.0.3494'

View file

@ -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<ScopeSyntax>();
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;
}

View file

@ -38,6 +38,20 @@ namespace PascalABCCompiler.SyntaxTree
AddSymbols(vd.vars.list, SymKind.var, vd.vars_type, attr);
base.visit(vd);
}
public override void visit(assign_var_tuple node)
{
AddSymbols(node.idents.idents, SymKind.var);
base.visit(node);
}
public override void visit(var_tuple_def_statement node)
{
var attr = node.var_attr == definition_attribute.Static ? SymbolAttributes.class_attr : 0;
AddSymbols(node.vars.list, SymKind.var, node.vars_type, attr);
base.visit(node);
}
public override void visit(formal_parameters fp)
{
foreach (var pg in fp.params_list)

View file

@ -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<ScopeSyntax> path;
public BindResult(SymInfoSyntax s, List<ScopeSyntax> p)
{
symInfo = s;
path = p;
}
}
public abstract class ScopeSyntax
{
public syntax_tree_node node;

View file

@ -0,0 +1,102 @@
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<statement> desugar(tuple_node tn, addressed_value_list vars)
{
var order = Assign.getAssignOrder(tn, vars, binder);
var assigns = new List<statement>();
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);
}
public override void visit(assign_var_tuple node)
{
if (node.expr is tuple_node tn)
{
var n = node.idents.idents.Count();
if (n > tn.el.Count)
throw new SyntaxVisitorError("TOO_MANY_ELEMENTS_ON_LEFT_SIDE_OF_TUPLE_ASSIGNMENT", node.idents.idents[0]);
var assigns = new List<statement>();
for(var i =0; i < n; i++)
{
var cur = new var_def_statement(node.idents.idents[i], tn.el.expressions[i], tn.Parent.source_context);
assigns.Add(new var_statement(cur, node.source_context));
}
ReplaceStatementUsingParent(node, assigns);
}
else
base.visit(node);
}
public override void visit(var_tuple_def_statement node)
{
if (node.inital_value is tuple_node tn)
{
var n = node.vars.idents.Count();
if (n > tn.el.Count)
throw new SyntaxVisitorError("TOO_MANY_ELEMENTS_ON_LEFT_SIDE_OF_TUPLE_ASSIGNMENT", node.vars.idents[0]);
var assigns = new List<var_def_statement>();
for (var i = 0; i < n; i++)
{
var cur = new var_def_statement(node.vars.idents[i], tn.el.expressions[i], tn.Parent.source_context);
assigns.Add(cur);
}
ReplaceVarTupleDefStatementUsingParent(node, assigns);
}
else
base.visit(node);
}
}
}

View file

@ -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<Assign> getAssignOrder(tuple_node tn, addressed_value_list vars, BindCollectLightSymInfo binder)
{
var left = new List<Symbol>();
var right = new List<Symbol>();
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();
}
}
}

View file

@ -0,0 +1,166 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace AssignTupleDesugarAlgorithm
{
internal class AssignGraph : IEnumerable<SymbolNode>
{
public readonly HashSet<SymbolNode> vertexes = new HashSet<SymbolNode>();
public readonly List<Edge> edges = new List<Edge>();
private bool AdjStructuresNeedSync = false;
public List<Edge> assignOrder = new List<Edge>();
public List<Edge> assignLast = new List<Edge>();
public List<Edge> assignFirst = new List<Edge>();
private Dictionary<SymbolNode, List<SymbolNode>> outAdjStructure = null;
public Dictionary<SymbolNode, List<SymbolNode>> OutAdjStructure
{
get
{
if (outAdjStructure == null || AdjStructuresNeedSync)
{
outAdjStructure = createOutAdjStructure();
inAdjStructure = createInAdjStructure();
AdjStructuresNeedSync = false;
}
return outAdjStructure;
}
}
private Dictionary<SymbolNode, List<SymbolNode>> createOutAdjStructure()
{
Dictionary<SymbolNode, List<SymbolNode>> res = new Dictionary<SymbolNode, List<SymbolNode>>();
foreach (var vert in vertexes) res.Add(vert, new List<SymbolNode>());
foreach (Edge edge in edges) res[edge.from].Add(edge.to);
return res;
}
private Dictionary<SymbolNode, List<SymbolNode>> inAdjStructure = null;
public Dictionary<SymbolNode, List<SymbolNode>> InAdjStructure
{
get
{
if (inAdjStructure == null || AdjStructuresNeedSync)
{
outAdjStructure = createOutAdjStructure();
inAdjStructure = createInAdjStructure();
AdjStructuresNeedSync = false;
}
return inAdjStructure;
}
}
private Dictionary<SymbolNode, List<SymbolNode>> createInAdjStructure()
{
Dictionary<SymbolNode, List<SymbolNode>> res = new Dictionary<SymbolNode, List<SymbolNode>>();
foreach (var vert in vertexes) res.Add(vert, new List<SymbolNode>());
foreach (Edge edge in edges) res[edge.to].Add(edge.from);
return res;
}
public List<Edge> GetInEdgesForVertex(SymbolNode v) => edges.FindAll(edge => edge.to == v);
public List<Edge> 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<Edge> GetAssignOrder()
{
List<Edge> assignOrder = new List<Edge>();
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<SymbolNode> 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<Edge> edges, List<SymbolNode> vertexes) : this(edges)
{
foreach (SymbolNode vert in vertexes) this.vertexes.Add(vert);
}
public AssignGraph()
{
}
public AssignGraph(List<Edge> e)
{
edges = e;
foreach (var edge in edges)
{
vertexes.Add(edge.from);
vertexes.Add(edge.to);
}
}
public IEnumerator<SymbolNode> GetEnumerator()
{
return ((IEnumerable<SymbolNode>)vertexes).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)vertexes).GetEnumerator();
}
}
}

View file

@ -0,0 +1,31 @@
using System.Collections.Generic;
namespace AssignTupleDesugarAlgorithm
{
internal class Cycle
{
List<SymbolNode> cycle;
public int Len => cycle.Count;
public Cycle(List<SymbolNode> 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];
}
}
}

View file

@ -0,0 +1,30 @@
using System;
namespace AssignTupleDesugarAlgorithm
{
internal class Edge : IEquatable<Edge>, IComparable<Edge>
{
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;
}
}
}

View file

@ -0,0 +1,525 @@
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<AssignGraph> OnEnterGraph,
Func<AssignGraph, SymbolNode, bool> OnProcessVertex,
Action<AssignGraph, SymbolNode> OnEnterNode,
Func<AssignGraph, SymbolNode, SymbolNode, bool> OnProcessNodeChild,
Action<AssignGraph, SymbolNode> OnLeaveNode,
Action<AssignGraph> OnLeaveGraph,
Func<AssignGraph, SymbolNode, IEnumerable<SymbolNode>> vertexTraversalOrderFunc = null,
IEnumerable<SymbolNode> graphTraversalOrder = null)
{
void dfs(SymbolNode start_node,
Action<AssignGraph, SymbolNode> OnEnter,
Func<AssignGraph, SymbolNode, SymbolNode, bool> OnProcessChild,
Action<AssignGraph, SymbolNode> OnLeave
)
{
OnEnter(graph, start_node);
IEnumerable<SymbolNode> 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<int> left = new HashSet<int>();
public HashSet<int> right = new HashSet<int>();
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<Symbol> left, List<Symbol> right)
{
if (right.Count < left.Count) throw new Exception("AAAA");
List<SymbolNode> v_left = new List<SymbolNode>();
List<SymbolNode> v_right = new List<SymbolNode>();
List<Symbol> left_visited = new List<Symbol>();
List<Edge> assign_first = new List<Edge>();
List<Edge> assign_last = new List<Edge>();
var moved_first = new List<Edge>();
var moved_last = new LinkedList<Edge>();
var movedAssigns = new HashSet<int>();
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 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;
}
}
// if repeated assigns or pointers -> 2n
for (int i = 0; i < left.Count(); i++)
{
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]);
}
//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--)
{
//handle 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);
}
}
var indexerGraph = new SynonymsGraph(SynonymsGraph.Type.INDEXER);
var nameToGraph = new Dictionary<string, SynonymsGraph>();
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<Symbol>(left);
var rightSet = new HashSet<Symbol>(right);
var graphs = new List<SynonymsGraph>();
graphs.AddRange(nameToGraph.Values);
graphs.Add(indexerGraph);
//Handle const
for (int i = 0; i < left.Count; i++)
{
if (right[i].type == Symbol.Type.CONST_EXPR)
{
moveAssign(i, false);
}
}
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<string, SymbolNode>();
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<Edge> assigns = new List<Edge>();
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<Edge> make2n(List<Symbol> left, List<Symbol> right)
{
var temps = new List<SymbolNode>();
var res = new List<Edge>();
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<Edge> newEdges = new List<Edge>();
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<List<SymbolNode>> findStronglyConnectedComponets(this AssignGraph g)
{
List<List<SymbolNode>> res = new List<List<SymbolNode>>();
Stack<SymbolNode> route = new Stack<SymbolNode>();
HashSet<SymbolNode> visited = new HashSet<SymbolNode>();
Action<AssignGraph> onEnterGraph = (_) =>
{
route.Clear();
visited.Clear();
};
Action<AssignGraph, SymbolNode> onEnterNode = (graph, node) => { visited.Add(node); };
Action<AssignGraph, SymbolNode> onLeaveNode = (graph, node) => { route.Push(node); };
Func<AssignGraph, SymbolNode, SymbolNode, bool> onProcessChildNode = (graph, parent, childNode) =>
{
if (!visited.Contains(childNode))
{
return true;
}
else return false;
};
Func<AssignGraph, SymbolNode, bool> onProcessVertex = (graph, vert) => { return !visited.Contains(vert); };
g.dfs(OnEnterGraph: onEnterGraph, OnProcessVertex: onProcessVertex,
OnEnterNode: onEnterNode, OnProcessNodeChild: onProcessChildNode,
OnLeaveNode: onLeaveNode,
OnLeaveGraph: (_) => { }
);
Action<AssignGraph> onEnterGraphReversed = (_) => { visited.Clear(); };
Action<AssignGraph, SymbolNode> onEnterNodeReversed = (graph, node) =>
{
visited.Add(node);
res.Last().Add(node);
};
Action<AssignGraph, SymbolNode> onLeaveNodeReversed = (graph, node) => { };
Func<AssignGraph, SymbolNode, SymbolNode, bool> onProcessChildNodeReversed = (graph, parent, childNode) =>
{
if (!visited.Contains(childNode))
{
return true;
}
else return false;
};
Func<AssignGraph, SymbolNode, bool> onProcessVertexReversed = (graph, vert) =>
{
if (visited.Contains(vert)) return false;
else
{
res.Add(new List<SymbolNode>());
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<Cycle> findAllUniqueElementaryCycles(this AssignGraph g)
{
var res = new List<Cycle>();
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<AssignGraph> enterGraph = (graph) => { graph.resetVertexesColor(); };
Func<AssignGraph, SymbolNode, bool> ProcessNode = (graph, node) => node.color != SymbolNode.Color.GREY;
Action<AssignGraph, SymbolNode> EnterNode = (graph, vert) => { vert.color = SymbolNode.Color.GREY; };
Func<AssignGraph, SymbolNode, SymbolNode, bool> 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();
}
}
}

View file

@ -0,0 +1,133 @@
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, CONST_EXPR
}
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 if(node is const_node)
{
type = Type.CONST_EXPR;
}
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<string>.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 => "<tup_opt>" + counter++;
public TempSymbol() : base(new ident(getTempPrefix), null)
{
type = Type.LOCAL;
}
}
}

View file

@ -0,0 +1,67 @@
using System;
namespace AssignTupleDesugarAlgorithm
{
internal class SymbolNode : IEquatable<SymbolNode>
{
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)
{
}
}
}

View file

@ -14,11 +14,14 @@ namespace SyntaxVisitors.SugarVisitors
/// </summary>
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()
{
@ -59,81 +62,97 @@ namespace SyntaxVisitors.SugarVisitors
// Остальное раскрывается в AssignTuplesDesugarVisitor
public override void visit(assign_var_tuple assvartup)
{
if (assvartup.expr is tuple_node tn && tn.el.expressions.All(ex => ex is const_node) && !tn.el.expressions.Any(ex => ex is nil_const))
if (assvartup.expr is tuple_node tn)
{
var n = assvartup.idents.idents.Count();
if (n > tn.el.Count)
throw new SyntaxVisitorError("TOO_MANY_ELEMENTS_ON_LEFT_SIDE_OF_TUPLE_ASSIGNMENT", assvartup.idents[0]);
// Оптимизация, т.к. все - константы
var sl = new List<statement>();
for (var i = 0; i < n; i++)
if (tn.el.expressions.All(ex => ex is const_node) && !tn.el.expressions.Any(ex => ex is nil_const))
{
var a = new var_statement(
assvartup.idents.idents[i],
tn.el.expressions[i],
assvartup.idents.idents[i].source_context);
sl.Add(a);
var n = assvartup.idents.idents.Count();
if (n > tn.el.Count)
throw new SyntaxVisitorError("TOO_MANY_ELEMENTS_ON_LEFT_SIDE_OF_TUPLE_ASSIGNMENT", assvartup.idents[0]);
// Оптимизация, т.к. все - константы
var sl = new List<statement>();
for (var i = 0; i < n; i++)
{
var a = new var_statement(
assvartup.idents.idents[i],
tn.el.expressions[i],
assvartup.idents.idents[i].source_context);
sl.Add(a);
}
ReplaceStatementUsingParent(assvartup, sl);
}
ReplaceStatementUsingParent(assvartup, sl);
else if (optimize_tuple_assign)
return;
else
DefaultVisit(assvartup);
}
else
{
DefaultVisit(assvartup);
}
}
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 (asstup.expr is tuple_node tn)
{
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<statement>();
for (var i = 0; i < n; i++)
if (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<statement>();
for (var i = 0; i < n; i++)
// Оптимизация, т.к. все - константы
var sl = new List<statement>();
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 (optimize_tuple_assign)
return;
else if (!tn.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 > tn.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 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);
}
var sl = new List<statement>();
for (var i = 0; i < n; i++)
{
var temp_id = new ident(UniqueName(), tn.el.expressions[i].source_context);
var var_def = new var_def_statement(temp_id, tn.el.expressions[i], tn.el.expressions[i].source_context);
var a = new var_statement(var_def, tn.el.expressions[i].source_context);
sl.Add(a);
}
ReplaceStatementUsingParent(asstup, sl);
//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
DefaultVisit(asstup);
}
else
{
DefaultVisit(asstup);
}
}
public void ReplaceVarTupleDefStatementUsingParent(var_tuple_def_statement from, IEnumerable<var_def_statement> to)
@ -150,27 +169,34 @@ namespace SyntaxVisitors.SugarVisitors
// А это по идее до begin - там немного другие узлы и внешний контекст
public override void visit(var_tuple_def_statement vtd)
{
if (vtd.inital_value is tuple_node tn && tn.el.expressions.All(ex => ex is const_node) && !tn.el.expressions.Any(ex => ex is nil_const))
{
var n = vtd.vars.idents.Count();
if (n > tn.el.Count)
throw new SyntaxVisitorError("TOO_MANY_ELEMENTS_ON_LEFT_SIDE_OF_TUPLE_ASSIGNMENT", vtd.vars.idents[0]);
var vd = new List<var_def_statement>();
for (var i = 0; i < n; i++)
if (vtd.inital_value is tuple_node tn) {
if (tn.el.expressions.All(ex => ex is const_node) && !tn.el.expressions.Any(ex => ex is nil_const))
{
var a = new var_def_statement(vtd.vars.idents[i],
tn.el.expressions[i],
vtd.vars.idents[i].source_context);
vd.Add(a);
var n = vtd.vars.idents.Count();
if (n > tn.el.Count)
throw new SyntaxVisitorError("TOO_MANY_ELEMENTS_ON_LEFT_SIDE_OF_TUPLE_ASSIGNMENT", vtd.vars.idents[0]);
var vd = new List<var_def_statement>();
for (var i = 0; i < n; i++)
{
var a = new var_def_statement(vtd.vars.idents[i],
tn.el.expressions[i],
vtd.vars.idents[i].source_context);
vd.Add(a);
}
ReplaceVarTupleDefStatementUsingParent(vtd, vd);
visit(vtd.inital_value);
}
ReplaceVarTupleDefStatementUsingParent(vtd, vd);
visit(vtd.inital_value);
else if (optimize_tuple_assign)
return;
else
DefaultVisit(vtd);
}
else
{
DefaultVisit(vtd);
}
}
public override void visit(tuple_node tup)

View file

@ -52,6 +52,14 @@
<Compile Include="LightSymInfoVisitors\CollectLightSymInfoVisitor.cs" />
<Compile Include="LightSymInfoVisitors\LightScopeCreator.cs" />
<Compile Include="LightSymInfoVisitors\LightScopeHelperClasses.cs" />
<Compile Include="SugarVisitors\AssignTuplesDesugarVisitor\desugarAlgoritm\Assign.cs" />
<Compile Include="SugarVisitors\AssignTuplesDesugarVisitor\desugarAlgoritm\AssignGraph.cs" />
<Compile Include="SugarVisitors\AssignTuplesDesugarVisitor\desugarAlgoritm\Cycle.cs" />
<Compile Include="SugarVisitors\AssignTuplesDesugarVisitor\desugarAlgoritm\Edge.cs" />
<Compile Include="SugarVisitors\AssignTuplesDesugarVisitor\desugarAlgoritm\GraphUtils.cs" />
<Compile Include="SugarVisitors\AssignTuplesDesugarVisitor\desugarAlgoritm\Symbol.cs" />
<Compile Include="SugarVisitors\AssignTuplesDesugarVisitor\desugarAlgoritm\SymbolNode.cs" />
<Compile Include="SugarVisitors\AssignTuplesDesugarVisitor\NewAssignTuplesDesugarVisitor.cs" />
<Compile Include="SugarVisitors\CacheFunctionVisitor.cs" />
<Compile Include="SugarVisitors\LetExprVisitor.cs" />
<Compile Include="SugarVisitors\TestAssignIsDefVisitor.cs" />
@ -65,7 +73,7 @@
<Compile Include="PatternsVisitors\PatternsDesugaringVisitor.cs" />
<Compile Include="PatternsVisitors\ReplaceNamesInIsVarVisitor.cs" />
<Compile Include="PatternsVisitors\SingleDeconstructChecker.cs" />
<Compile Include="SugarVisitors\AssignTuplesDesugarVisitor.cs" />
<Compile Include="SugarVisitors\AssignTuplesDesugarVisitor\AssignTuplesDesugarVisitor.cs" />
<Compile Include="SugarVisitors\DoubleQuestionDesugarVisitor.cs" />
<Compile Include="SugarVisitors\IndexVisitor.cs" />
<Compile Include="SugarVisitors\LoopDesugarVisitor.cs" />

View file

@ -113,13 +113,16 @@ namespace SyntaxVisitors
return;
}
var newNameCache = new Dictionary<string, string>();
var newLocalNames = 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;
//BlockNamesStack[CurrentLevel][low] = newName;
newNameCache[low] = newName;
return new ident(newName, id.source_context);
});
@ -130,6 +133,8 @@ namespace SyntaxVisitors
Replace(var_def, newVS);
listNodes[listNodes.Count - 1] = newVS; //SSM 8.11.18
ProcessNode(newVS.inital_value); // SSM 10.06.2020 #2103
foreach (var key in newNameCache.Keys)
BlockNamesStack[CurrentLevel][key] = newNameCache[key];
//base.visit(newVS); // SSM 10.06.2020 - зачем обходить всё?
}

View file

@ -10,7 +10,6 @@ unit ABCObjects;
//{$apptype windows}
{$reference '%GAC%\System.Windows.Forms.dll'}
{$reference '%GAC%\System.Drawing.dll'}
{$gendoc true}
interface

View file

@ -7,7 +7,6 @@
unit CRT;
{$apptype console}
{$gendoc true}
interface

View file

@ -8,7 +8,6 @@ unit GraphABC;
{$apptype windows}
{$reference '%GAC%\System.Windows.Forms.dll'}
{$reference '%GAC%\System.Drawing.dll'}
{$gendoc true}
interface

View file

@ -7,8 +7,6 @@ unit PABCSystem;
{$zerobasedstrings off}
{$gendoc true}
// Default Application type
{$apptype console}
@ -1432,24 +1430,26 @@ function ParamStr(i: integer): string;
/// Возвращает текущий каталог
function GetDir: string;
/// Меняет текущий каталог
procedure ChDir(s: string);
procedure ChDir(dirName: string);
/// Создает каталог
procedure MkDir(s: string);
procedure MkDir(dirName: string);
/// Удаляет каталог
procedure RmDir(s: string);
procedure RmDir(dirName: string);
/// Создает каталог. Возвращает True, если каталог успешно создан
function CreateDir(s: string): boolean;
function CreateDir(dirName: string): boolean;
/// Удаляет файл. Если файл не может быть удален, то возвращает False
function DeleteFile(fname: string): boolean;
function DeleteFile(fileName: string): boolean;
/// Возвращает текущий каталог
function GetCurrentDir: string;
/// Удаляет каталог. Возвращает True, если каталог успешно удален
function RemoveDir(s: string): boolean;
function RemoveDir(dirName: string): boolean;
/// Переименовывает файл fileName, давая ему новое имя newfileName. Возвращает True, если файл успешно переименован
function RenameFile(fileName, newfileName: string): boolean;
/// Переименовывает каталог dirName, давая ему новое имя newDirName. Возвращает True, если каталог успешно переименован
function RenameDirectory(dirName, newDirName: string): boolean;
/// Устанавливает текущий каталог. Возвращает True, если каталог успешно удален
function SetCurrentDir(s: string): boolean;
function SetCurrentDir(dirName: string): boolean;
/// Изменяет расширение файла с именем fileName на newExt
function ChangeFileNameExtension(fileName, newExt: string): string;
@ -1464,9 +1464,9 @@ procedure Assert(cond: boolean; sourceFile: string := ''; line: integer := 0);
procedure Assert(cond: boolean; message: string; sourceFile: string := ''; line: integer := 0);
/// Возвращает свободное место в байтах на диске с именем diskname
function DiskFree(diskname: string): int64;
function DiskFree(diskName: string): int64;
/// Возвращает размер в байтах на диске с именем diskname
function DiskSize(diskname: string): int64;
function DiskSize(diskName: string): int64;
/// Возвращает свободное место в байтах на диске disk. disk=0 - текущий диск, disk=1 - диск A: , disk=2 - диск B: и т.д.
function DiskFree(disk: integer): int64;
/// Возвращает размер в байтах на диске disk. disk=0 - текущий диск, disk=1 - диск A: , disk=2 - диск B: и т.д.
@ -8461,41 +8461,41 @@ begin
Result := Environment.CurrentDirectory;
end;
procedure ChDir(s: string);
procedure ChDir(dirName: string);
begin
Environment.CurrentDirectory := s;
Environment.CurrentDirectory := dirName;
end;
procedure MkDir(s: string);
procedure MkDir(dirName: string);
begin
Directory.CreateDirectory(s);
Directory.CreateDirectory(dirName);
end;
procedure RmDir(s: string);
procedure RmDir(dirName: string);
begin
Directory.Delete(s);
Directory.Delete(dirName);
end;
function CreateDir(s: string): boolean;
function CreateDir(dirName: string): boolean;
begin
try
Result := True;
Directory.CreateDirectory(s);
Directory.CreateDirectory(dirName);
except
Result := False;
end;
end;
function DeleteFile(fname: string): boolean;
function DeleteFile(fileName: string): boolean;
begin
if not &File.Exists(fname) then
if not &File.Exists(fileName) then
begin
Result := False;
exit
end;
try
Result := True;
&File.Delete(fname);
&File.Delete(fileName);
except
Result := False;
end;
@ -8506,11 +8506,11 @@ begin
Result := Environment.CurrentDirectory;
end;
function RemoveDir(s: string): boolean;
function RemoveDir(dirName: string): boolean;
begin
try
Result := True;
Directory.Delete(s);
Directory.Delete(dirName);
except
Result := False;
end;
@ -8526,11 +8526,21 @@ begin
end;
end;
function SetCurrentDir(s: string): boolean;
function RenameDirectory(dirName, newDirName: string): boolean;
begin
try
Result := True;
Environment.CurrentDirectory := s;
Directory.Move(dirName, newDirName);
except
Result := False;
end;
end;
function SetCurrentDir(dirName: string): boolean;
begin
try
Result := True;
Environment.CurrentDirectory := dirName;
except
Result := False;
end;
@ -8598,7 +8608,7 @@ begin
System.Diagnostics.Contracts.Contract.Assert(cond,'Файл '+sourceFile+', строка '+line.ToString() + ': ' + message)
end;
function DiskFree(diskname: string): int64;
function DiskFree(diskName: string): int64;
begin
try
var d := new System.IO.DriveInfo(diskname);
@ -8608,7 +8618,7 @@ begin
end;
end;
function DiskSize(diskname: string): int64;
function DiskSize(diskName: string): int64;
begin
try
var d := new System.IO.DriveInfo(diskname);
@ -13745,6 +13755,23 @@ begin
Result := Self.Split(delims.ToCharArray, System.StringSplitOptions.RemoveEmptyEntries);
end;
/// Преобразует многострочную строку в массив строк
function ToLines(Self: string): array of string; extensionmethod;
begin
Result := Self.Split(|
#13#10, // CR+LF: Win
#10, // LF: Linux
#13 // CR: Mac
// https://en.m.wikipedia.org/wiki/Newline#Unicode
// But standard .Net things like System.IO.StringReader don't support these, so for now - commented out
// #11, // Vertical Tab
// #12, // Form feed
// char($85), // Next Line
// char($2028), // Line Separator
// char($2029), // Paragraph SeparatorS
|, System.StringSplitOptions.None);
end;
procedure PassSpaces(var s: string; var from: integer);
begin
while (from <= s.Length) and char.IsWhiteSpace(s[from]) do

View file

@ -0,0 +1,8 @@
//!Повторно объявленный идентификатор name
type
name = (name);
begin
// Ожидался тип
var v1: name;
end.

View file

@ -0,0 +1,11 @@
//!Количество полей не совпадает с количеством полей в записи
type
r1 = record
a: byte;
static b: word;
end;
begin
// Работает, но не должно
var a: r1 := (a: 1; b: 2);
end.

11
TestSuite/recinit3.pas Normal file
View file

@ -0,0 +1,11 @@
type
r1 = record
a: byte;
static b: word;
end;
begin
//Ошибка: Количество полей не совпадает с количеством полей в записи
var a: r1 := (a: 1);
assert(a.a = 1);
end.

View file

@ -0,0 +1,10 @@
unit u_events6;
uses System;
type
I1 = interface
event E1: Action;
end;
end.

View file

@ -0,0 +1,20 @@
uses u_events6, System;
type TClass = class(I1)
event E1: Action;
procedure RaiseMethod;
begin
if E1 <> nil then
E1();
end;
end;
begin
var i := 0;
var o := new TClass;
o.E1 += () -> begin Inc(i) end;
o.RaiseMethod();
assert(i = 1);
end.

29
TestSuite/yield8.pas Normal file
View file

@ -0,0 +1,29 @@
function V(n: byte) := 1;
function f1: sequence of integer;
begin
//Ошибка: Ожидалось имя процедуры или функции
var v := V(0);
// Если закомментировать эту строчку - предыдущая строчка работает
yield v;
end;
function f2: sequence of integer;
begin
//Ошибка: Ожидалось имя процедуры или функции
var v := V(0);
var w := v + 1;
// Если закомментировать эту строчку - предыдущая строчка работает
yield w;
end;
begin
var i := 0;
foreach var j in f1 do
i := j;
assert(i = 1);
foreach var j in f2 do
i := j;
assert(i = 2);
end.

View file

@ -12211,6 +12211,8 @@ namespace PascalABCCompiler.TreeConverter
context.check_name_free(name, loc);
is_direct_type_decl = true;
type_node tn = convert_strong(_type_declaration.type_def);
if (_type_declaration.type_def is enum_type_definition)
context.check_name_free(name, loc);
assign_doc_info(tn,_type_declaration);
is_direct_type_decl = false;
if (_type_declaration.type_def is SyntaxTree.named_type_reference||
@ -15595,13 +15597,14 @@ namespace PascalABCCompiler.TreeConverter
private record_initializer ConvertRecordInitializer(common_type_node ctn, record_initializer constant)
{
location loc = constant.location;
var non_static_fields = ctn.fields.Where(x => !x.IsStatic).ToArray();
if (!ctn.is_value_type)
AddError(loc, "RECORD_CONST_NOT_ALLOWED_{0}", ctn.name);
if (ctn.fields.Count != constant.record_const_definition_list.Count)
if (non_static_fields.Length != constant.record_const_definition_list.Count)
AddError(loc, "INVALID_RECORD_CONST_FIELD_COUNT");
constant.type = ctn;
constant.field_values.Clear();
for (int i = 0; i < ctn.fields.Count; i++)
for (int i = 0; i < non_static_fields.Length; i++)
{
class_field cf = ctn.fields[i];
if (cf.name.ToLower() != constant.record_const_definition_list[i].name.name.ToLower())

Binary file not shown.

View file

@ -1430,24 +1430,26 @@ function ParamStr(i: integer): string;
/// Возвращает текущий каталог
function GetDir: string;
/// Меняет текущий каталог
procedure ChDir(s: string);
procedure ChDir(dirName: string);
/// Создает каталог
procedure MkDir(s: string);
procedure MkDir(dirName: string);
/// Удаляет каталог
procedure RmDir(s: string);
procedure RmDir(dirName: string);
/// Создает каталог. Возвращает True, если каталог успешно создан
function CreateDir(s: string): boolean;
function CreateDir(dirName: string): boolean;
/// Удаляет файл. Если файл не может быть удален, то возвращает False
function DeleteFile(fname: string): boolean;
function DeleteFile(fileName: string): boolean;
/// Возвращает текущий каталог
function GetCurrentDir: string;
/// Удаляет каталог. Возвращает True, если каталог успешно удален
function RemoveDir(s: string): boolean;
function RemoveDir(dirName: string): boolean;
/// Переименовывает файл fileName, давая ему новое имя newfileName. Возвращает True, если файл успешно переименован
function RenameFile(fileName, newfileName: string): boolean;
/// Переименовывает каталог dirName, давая ему новое имя newDirName. Возвращает True, если каталог успешно переименован
function RenameDirectory(dirName, newDirName: string): boolean;
/// Устанавливает текущий каталог. Возвращает True, если каталог успешно удален
function SetCurrentDir(s: string): boolean;
function SetCurrentDir(dirName: string): boolean;
/// Изменяет расширение файла с именем fileName на newExt
function ChangeFileNameExtension(fileName, newExt: string): string;
@ -1462,9 +1464,9 @@ procedure Assert(cond: boolean; sourceFile: string := ''; line: integer := 0);
procedure Assert(cond: boolean; message: string; sourceFile: string := ''; line: integer := 0);
/// Возвращает свободное место в байтах на диске с именем diskname
function DiskFree(diskname: string): int64;
function DiskFree(diskName: string): int64;
/// Возвращает размер в байтах на диске с именем diskname
function DiskSize(diskname: string): int64;
function DiskSize(diskName: string): int64;
/// Возвращает свободное место в байтах на диске disk. disk=0 - текущий диск, disk=1 - диск A: , disk=2 - диск B: и т.д.
function DiskFree(disk: integer): int64;
/// Возвращает размер в байтах на диске disk. disk=0 - текущий диск, disk=1 - диск A: , disk=2 - диск B: и т.д.
@ -8459,41 +8461,41 @@ begin
Result := Environment.CurrentDirectory;
end;
procedure ChDir(s: string);
procedure ChDir(dirName: string);
begin
Environment.CurrentDirectory := s;
Environment.CurrentDirectory := dirName;
end;
procedure MkDir(s: string);
procedure MkDir(dirName: string);
begin
Directory.CreateDirectory(s);
Directory.CreateDirectory(dirName);
end;
procedure RmDir(s: string);
procedure RmDir(dirName: string);
begin
Directory.Delete(s);
Directory.Delete(dirName);
end;
function CreateDir(s: string): boolean;
function CreateDir(dirName: string): boolean;
begin
try
Result := True;
Directory.CreateDirectory(s);
Directory.CreateDirectory(dirName);
except
Result := False;
end;
end;
function DeleteFile(fname: string): boolean;
function DeleteFile(fileName: string): boolean;
begin
if not &File.Exists(fname) then
if not &File.Exists(fileName) then
begin
Result := False;
exit
end;
try
Result := True;
&File.Delete(fname);
&File.Delete(fileName);
except
Result := False;
end;
@ -8504,11 +8506,11 @@ begin
Result := Environment.CurrentDirectory;
end;
function RemoveDir(s: string): boolean;
function RemoveDir(dirName: string): boolean;
begin
try
Result := True;
Directory.Delete(s);
Directory.Delete(dirName);
except
Result := False;
end;
@ -8524,11 +8526,21 @@ begin
end;
end;
function SetCurrentDir(s: string): boolean;
function RenameDirectory(dirName, newDirName: string): boolean;
begin
try
Result := True;
Environment.CurrentDirectory := s;
Directory.Move(dirName, newDirName);
except
Result := False;
end;
end;
function SetCurrentDir(dirName: string): boolean;
begin
try
Result := True;
Environment.CurrentDirectory := dirName;
except
Result := False;
end;
@ -8596,7 +8608,7 @@ begin
System.Diagnostics.Contracts.Contract.Assert(cond,'Файл '+sourceFile+', строка '+line.ToString() + ': ' + message)
end;
function DiskFree(diskname: string): int64;
function DiskFree(diskName: string): int64;
begin
try
var d := new System.IO.DriveInfo(diskname);
@ -8606,7 +8618,7 @@ begin
end;
end;
function DiskSize(diskname: string): int64;
function DiskSize(diskName: string): int64;
begin
try
var d := new System.IO.DriveInfo(diskname);
@ -13743,6 +13755,23 @@ begin
Result := Self.Split(delims.ToCharArray, System.StringSplitOptions.RemoveEmptyEntries);
end;
/// Преобразует многострочную строку в массив строк
function ToLines(Self: string): array of string; extensionmethod;
begin
Result := Self.Split(|
#13#10, // CR+LF: Win
#10, // LF: Linux
#13 // CR: Mac
// https://en.m.wikipedia.org/wiki/Newline#Unicode
// But standard .Net things like System.IO.StringReader don't support these, so for now - commented out
// #11, // Vertical Tab
// #12, // Form feed
// char($85), // Next Line
// char($2028), // Line Separator
// char($2029), // Paragraph SeparatorS
|, System.StringSplitOptions.None);
end;
procedure PassSpaces(var s: string; var from: integer);
begin
while (from <= s.Length) and char.IsWhiteSpace(s[from]) do