pascalabcnet/SyntaxVisitors/SugarVisitors/TupleVisitor.cs
Александр Земляк 1f4b44c2c0
Исправление ошибок при перекомпиляции pcu файлов (#3398)
* Move yield string constants to StringConstants class

* Add GeneratedNamesManager class to store all generated names during unit compilation

* Make GeneratedNamesManager class non static

* Add Utils project to installer files

* Add GeneratedNamesManager usage in CapturedVariablesSubstitutionsManager

* Move GeneratedNamesManager initialization in CompilationUnit constructor

* More GeneratedNamesManager usage

* QuestionPointDesugarVisitor fix
2026-03-08 18:30:33 +03:00

218 lines
8.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) Ivan Bondarev, Stanislav Mikhalkovich (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PascalABCCompiler.CoreUtils;
using PascalABCCompiler.SyntaxTree;
namespace SyntaxVisitors.SugarVisitors
{
/// <summary>
/// А здесь не только замена кортежа - здесь оптимизация (a,b) := (1,2) и var (a,b) := (1,2)
/// </summary>
public class TupleVisitor : BaseChangeVisitor
{
public bool optimize_tuple_assign;
private readonly GeneratedNamesManager generatedNamesManager;
public TupleVisitor(bool optimize_tup_opt, GeneratedNamesManager generatedNamesManager)
{
optimize_tuple_assign = optimize_tup_opt;
this.generatedNamesManager = generatedNamesManager;
}
public static TupleVisitor Create(bool optimize_tup_opt, GeneratedNamesManager generatedNamesManager) => new TupleVisitor(optimize_tup_opt, generatedNamesManager);
private string UniqueName()
{
return generatedNamesManager.GenerateName("#tup_vis");
}
public override void visit(read_accessor_name wn)
{
DefaultVisit(wn);
}
public override void visit(unnamed_type_object u)
{
DefaultVisit(u);
}
public override void visit(name_assign_expr_list ne)
{
DefaultVisit(ne);
}
public override void visit(name_assign_expr ne)
{
DefaultVisit(ne);
}
public override void visit(labeled_statement ls)
{
var sl = new List<statement>();
sl.Add(new labeled_statement(ls.label_name, new empty_statement(), ls.source_context));
sl.Add(ls.to_statement);
ReplaceStatementUsingParent(ls, sl);
ProcessNode(ls.to_statement);
}
// Здесь раскрываются ситуации когда справа - кортеж с константами: var (a,b) := (1,2) и только это.
// Остальное раскрывается в AssignTuplesDesugarVisitor
public override void visit(assign_var_tuple assvartup)
{
if (assvartup.expr is tuple_node tn)
{
if (tn.el.expressions.All(ex => ex is const_node) && !tn.el.expressions.Any(ex => ex is nil_const))
{
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);
}
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)
{
if (tn.el.expressions.All(ex => ex is const_node) && !tn.el.expressions.Any(ex => ex is nil_const))
{
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 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 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 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);
}
//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)
{
foreach (var x in to)
x.Parent = from.Parent;
var sl = from.Parent as variable_definitions;
if (sl != null)
{
sl.ReplaceInList(from, to);
}
}
// А это по идее до begin - там немного другие узлы и внешний контекст
public override void visit(var_tuple_def_statement vtd)
{
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 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);
}
else if (optimize_tuple_assign)
return;
else
DefaultVisit(vtd);
}
else
{
DefaultVisit(vtd);
}
}
public override void visit(tuple_node tup)
{
var dn = new dot_node(new dot_node(new ident("?System", tup.source_context), new ident("Tuple", tup.source_context), tup.source_context), new ident("Create", tup.source_context));
var mc = new method_call(dn, tup.el, tup.source_context);
//var sug = new sugared_expression(tup, mc, tup.source_context); - нет никакой семантической проверки - всё - на уровне синтаксиса!
//ReplaceUsingParent(tup, mc); - исправление #1199. Оказывается, ReplaceUsingParent и Replace не эквивалентны - у копии Parent на старого родителя
Replace(tup, mc);
visit(mc);
}
}
}