pascalabcnet/SyntaxVisitors/PatternsVisitors/PatternsDesugaringVisitor.cs

1189 lines
53 KiB
C#
Raw Normal View History

2017-02-20 13:57:13 +03:00
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using PascalABCCompiler.CoreUtils;
2017-02-20 13:57:13 +03:00
using PascalABCCompiler.SyntaxTree;
2020-07-14 13:05:02 +03:00
namespace SyntaxVisitors.PatternsVisitors
2017-02-20 13:57:13 +03:00
{
2018-05-11 13:46:59 +03:00
// Patterns
2018-05-06 20:09:36 +03:00
public class DeconstructionDesugaringResult
{
/// <summary>
/// Переменная, имеющая тип паттерна
/// </summary>
public var_statement CastVariableDefinition { get; set; }
2018-05-15 17:55:57 +03:00
/// <summary>
/// Переменная, в которую сохраняется результат мэтчинга
/// </summary>
public var_statement SuccessVariableDefinition { get; set; }
2018-05-06 20:09:36 +03:00
/// <summary>
/// Переменные, полученные в результате деконструкции
/// </summary>
public List<var_def_statement> DeconstructionVariables { get; private set; } = new List<var_def_statement>();
/// <summary>
/// Проверка соответствия типа выражения типу паттерна
/// </summary>
public expression TypeCastCheck { get; set; }
/// <summary>
/// Вызов Deconstruct
/// </summary>
public statement DeconstructCall { get; set; }
2018-05-15 17:55:57 +03:00
public ident CastVariable => CastVariableDefinition.var_def.vars.list.First();
public ident SuccessVariable => SuccessVariableDefinition.var_def.vars.list.First();
public List<statement> GetDeconstructionDefinitions(SourceContext patternContext)
2018-05-06 20:09:36 +03:00
{
2018-05-16 00:26:07 +03:00
var result = new List<statement>();
result.Add(CastVariableDefinition);
result.Add(new desugared_deconstruction(DeconstructionVariables, CastVariable, patternContext));
2018-05-15 17:55:57 +03:00
result.Add(SuccessVariableDefinition);
return result;
}
public if_node GetPatternCheckWithDeconstrunctorCall()
{
return SubtreeCreator.CreateIf(
TypeCastCheck,
new statement_list(new assign(SuccessVariable.name, true), DeconstructCall));
2018-05-06 20:09:36 +03:00
}
}
public class CollectionDesugaringResult
{
public List<statement> VarParametersDeclarations { get; set; } = new List<statement>();
public expression SuccessMatchingCheck { get; set; }
public expression CollectionLengthCheck { get; set; }
public List<semantic_check_sugared_statement_node> ElemTypeChecks { get; set; } = new List<semantic_check_sugared_statement_node>();
}
public class TupleDesugaringResult
{
public List<statement> VarParametersDeclarations { get; set; } = new List<statement>();
public expression SuccessMatchingCheck { get; set; }
public statement TupleLengthCheck { get; set; }
public List<semantic_check_sugared_statement_node> ElemTypeChecks { get; set; } = new List<semantic_check_sugared_statement_node>();
}
public class PatternsDesugaringVisitor : BaseChangeVisitor
2017-02-20 13:57:13 +03:00
{
2018-05-15 17:55:57 +03:00
private enum PatternLocation { Unknown, IfCondition, Assign }
private const string DeconstructMethodName = "deconstruct";
private const string IsTestMethodName = "__TypeCheckAndAssignForIsMatch";
private const string WildCardsTupleEqualFunctionName = "__WildCardsTupleEqual";
private const string SeqFunctionName = "Seq";
private const string CountPropertyName = "Count";
2018-05-15 17:55:57 +03:00
private const string GeneratedPatternNamePrefix = "<>pattern";
private const string GeneratedDeconstructParamPrefix = "<>deconstructParam";
2019-06-15 12:15:21 +03:00
private const string GeneratedVisitElseBranchVariableName = "<>visitElseBranch";
2020-03-09 14:14:53 +03:00
private const string GeneratedMatchExprVariableName = "<>matchExprVariable";
2018-05-06 20:09:36 +03:00
private if_node _previousIf;
2018-05-11 19:26:55 +03:00
private statement desugaredMatchWith;
2018-05-16 00:26:07 +03:00
private List<if_node> processedIfNodes = new List<if_node>();
2020-03-09 14:14:53 +03:00
private var_statement matchedExprVarDeclaration;
2018-05-16 00:26:07 +03:00
//const matching
private List<statement> typeChecks = new List<statement>();
private readonly GeneratedNamesManager generatedNamesManager;
private PatternsDesugaringVisitor(GeneratedNamesManager generatedNamesManager)
{
this.generatedNamesManager = generatedNamesManager;
}
public static PatternsDesugaringVisitor Create(GeneratedNamesManager generatedNamesManager) => new PatternsDesugaringVisitor(generatedNamesManager);
2017-02-20 13:57:13 +03:00
public override void visit(match_with matchWith)
{
desugaredMatchWith = null;
_previousIf = null;
2017-02-20 13:57:13 +03:00
var matchExprVariableName = NewMatchExprVariableId();
2020-03-09 14:14:53 +03:00
matchedExprVarDeclaration = new var_statement(
new ident(matchExprVariableName, matchWith.expr.source_context),
2020-03-09 14:14:53 +03:00
matchWith.expr.Clone() as expression,
matchWith.expr.source_context);
ReplaceUsingParent(matchWith.expr, new ident(matchExprVariableName, matchWith.expr.source_context));
2020-03-09 14:14:53 +03:00
/*if (matchWith.Parent is statement_list stl)
{
stl.InsertBefore(matchWith, matchedExprVarDeclaration);
}
else
{
var enclosingStl = new statement_list(matchedExprVarDeclaration, matchWith);
enclosingStl.source_context = matchWith.source_context;
ReplaceUsingParent(matchWith, enclosingStl);
}*/
// Кэшируем выражение для однократного вычисления
//var cachedExpression = NewGeneralName();
//AddDefinitionsInUpperStatementList(matchWith, new[] { new var_statement(cachedExpression, matchWith.expr) });
// Преобразование из сахара в известную конструкцию каждого case
2018-12-13 11:11:07 +03:00
var usedDeconstructionTypes = new HashSet<string>();
2017-02-20 13:57:13 +03:00
foreach (var patternCase in matchWith.case_list.elements)
{
2018-05-11 13:46:59 +03:00
if (patternCase == null)
continue;
2020-02-10 15:51:16 +03:00
DefaultDesugarPattern(matchWith.expr, patternCase);
2018-05-01 19:29:39 +03:00
2020-02-10 15:51:16 +03:00
/*switch (patternCase.pattern)
2018-12-13 11:11:07 +03:00
{
case deconstructor_pattern pattern:
2018-12-13 11:11:07 +03:00
{
// Проверяем встречался ли уже такой тип при деконструкции
// SSM 02.01.19 пока закомментировал этот кусок т.к. при этом коде падает стандартный пример ArithmSimplify.cs. #1408 снова открыл
2020-02-10 15:51:16 +03:00
var deconstructionType = (patternCase.pattern as deconstructor_pattern).
type as named_type_reference;
if (deconstructionType != null &&
deconstructionType.names != null &&
deconstructionType.names.Count != 0)
{
var deconstructionTypeName = deconstructionType.names[0].name;
if (usedDeconstructionTypes.Contains(deconstructionTypeName))
{
throw new SyntaxVisitorError("REPEATED_DECONSTRUCTION_TYPE",
patternCase.pattern.source_context);
}
usedDeconstructionTypes.Add(deconstructionTypeName);
2020-02-10 15:51:16 +03:00
}
2020-02-10 15:51:16 +03:00
DefaultDesugarPattern(matchWith.expr, patternCase);
break;
}
case const_pattern p:
{
2020-02-10 15:51:16 +03:00
//DesugarConstPatternCase(matchWith.expr, patternCase);
DefaultDesugarPattern(matchWith.expr, patternCase);
break;
}
case collection_pattern p:
{
2019-06-15 12:15:21 +03:00
DefaultDesugarPattern(matchWith.expr, patternCase);
break;
}
case tuple_pattern p:
{
2019-06-15 12:15:21 +03:00
DefaultDesugarPattern(matchWith.expr, patternCase);
break;
2018-12-13 11:11:07 +03:00
}
2020-02-10 15:51:16 +03:00
}*/
2017-02-20 13:57:13 +03:00
}
2018-05-01 19:29:39 +03:00
2018-05-11 19:26:55 +03:00
if (matchWith.defaultAction != null)
AddDefaultCase(matchWith.defaultAction as statement_list);
2018-05-01 19:29:39 +03:00
if (desugaredMatchWith == null)
2018-05-11 19:26:55 +03:00
desugaredMatchWith = new empty_statement();
2018-05-01 19:29:39 +03:00
if (typeChecks.Count != 0)
{
typeChecks.Add(desugaredMatchWith);
desugaredMatchWith = new statement_list(typeChecks);
}
2019-06-15 12:15:21 +03:00
2020-03-09 14:14:53 +03:00
desugaredMatchWith = new statement_list(matchedExprVarDeclaration, desugaredMatchWith);
// Замена выражения match with на новое несахарное поддерево и его обход
ReplaceUsingParent(matchWith, desugaredMatchWith);
visit(desugaredMatchWith);
2020-07-14 18:55:14 +03:00
typeChecks.Clear(); // SSM исправление ошибки #2276
2017-02-20 13:57:13 +03:00
}
private string GenerateNewName(string name)
2020-06-21 17:12:43 +03:00
{
return generatedNamesManager.GenerateName("$RenIsVarYield", "$" + name);
2020-06-21 17:12:43 +03:00
}
2020-07-14 13:05:02 +03:00
public override void visit(procedure_definition pd)
{
//ReplaceNamesInIsVarVisitor.New.ProcessNode(pd);
base.visit(pd);
}
public class StrBool
{
public string name;
public bool needtorename;
public StrBool(string name, bool b = false)
{
this.name = name;
this.needtorename = b;
}
}
public override void visit(is_pattern_expr isPatternExpr)
{
2018-06-17 14:21:52 +03:00
if (GetLocation(isPatternExpr) == PatternLocation.Unknown)
throw new SyntaxVisitorError("PATTERN_MATHING_IS_NOT_SUPPORTED_IN_THIS_CONTEXT", isPatternExpr.source_context);
Debug.Assert(GetAscendant<statement_list>(isPatternExpr) != null, "Couldn't find statement list in upper nodes");
2020-07-14 13:05:02 +03:00
//syntax_tree_node stn = isPatternExpr;
//while (stn != null && !(stn is procedure_definition) && !(stn is function_lambda_definition))
// stn = stn.Parent;
//if (stn is procedure_definition pd && pd.has_yield) // SSM 11/07/20 Надо сделать обход с переименованием!!! Сейчас and не работает в yield + ... is A(var a1)
//{
2020-07-14 18:55:14 +03:00
// SSM 14/07/20
2020-07-14 13:05:02 +03:00
var dict = new Dictionary<string, StrBool>(StringComparer.InvariantCultureIgnoreCase); // bool - надо ли начинать renaming
2020-07-14 18:55:14 +03:00
var l = new List<statement>();
var pp = isPatternExpr.right?.parameters;
if (pp != null)
{
foreach (var p in pp)
2020-06-21 17:12:43 +03:00
{
2020-07-14 18:55:14 +03:00
if (p is var_deconstructor_parameter vdp)
2020-06-21 17:12:43 +03:00
{
2020-07-14 18:55:14 +03:00
var idClone = vdp.identifier.TypedClone();
var idNew = vdp.identifier.TypedClone();
idNew.name = GenerateNewName(idNew.name);
var vs = new var_statement(idClone, idNew, idClone.source_context);
// проверять, что имя не начинается с $ и если начинается, то ошибка - уже переименовывали такое же!
if (vdp.identifier.name.StartsWith("$")) // SSM 14/07/20
throw new SyntaxVisitorError("Повторное объявление", vdp.identifier.source_context);
dict.Add(vdp.identifier.name, new StrBool(idNew.name,false));
//vdp.identifier.name = idNew.name; // в следующем цикле переименую
l.Add(vs);
2020-06-21 17:12:43 +03:00
}
}
2020-07-14 18:55:14 +03:00
}
2020-06-21 17:12:43 +03:00
2020-07-14 18:55:14 +03:00
// SSM 14/07/20
2020-07-14 13:05:02 +03:00
// подниматься пока выражение
syntax_tree_node ex = isPatternExpr;
syntax_tree_node ex1 = ex;
while (ex is expression)
{
ex1 = ex;
ex = ex.Parent;
}
ex = ex1; // саммое внешнее выражение. В нём и переименовать!
var nams = ex.DescendantNodes().Where(stn => stn is ident).Cast<ident>();
foreach (var n in nams)
{
// надо как-то пробросить до первой is_var
if (dict.ContainsKey(n.name))
{
var isvar = GetAscendant<is_pattern_expr>(n);
if (isvar != null)
{
dict[n.name].needtorename = true; // всё - можно переименовывать далее
n.name = dict[n.name].name; // переименовываем в is_var
continue;
}
if (!dict[n.name].needtorename) // если еще не дошли до is_var, то это какая-то внешняя и не переименовываем
continue;
if (n.Parent is dot_node dn && dn.left == n) // т.е. не переименовываем только если ident - в правой части dot_node
{
// переименовываем
n.name = dict[n.name].name;
}
else
{
// переименовываем
n.name = dict[n.name].name;
}
}
}
2020-07-14 18:55:14 +03:00
var stat = GetAscendant<statement>(isPatternExpr);
if (stat is if_node ifn)
{
//l.Add(ifn.then_body);
if (!(ifn.then_body is statement_list))
2020-06-21 17:12:43 +03:00
{
2020-07-14 18:55:14 +03:00
ifn.then_body = new statement_list(ifn.then_body);
2020-06-21 17:12:43 +03:00
}
2020-07-14 18:55:14 +03:00
(ifn.then_body as statement_list).list.InsertRange(0, l);
}
else
{
l.Insert(0, stat);
foreach (var x in l)
x.Parent = stat.Parent;
var stl = stat.Parent as statement_list;
2021-02-08 22:30:22 +03:00
if (stl != null)
stl.ReplaceInList(stat, l);
2021-02-09 09:15:54 +03:00
else if (stat is var_statement)
2021-02-08 22:30:22 +03:00
throw new SyntaxVisitorError("EMBEDDED_STATEMENT_CANNOT_BE_A_DECLARATION", stat.source_context);
2020-07-14 18:55:14 +03:00
}
2020-07-14 13:05:02 +03:00
//}
2020-06-21 17:12:43 +03:00
2020-06-21 17:12:43 +03:00
//ReplaceStatement(stat, l); // тут нельзя.
2018-05-16 00:26:07 +03:00
DesugarIsExpression(isPatternExpr);
}
2019-06-15 12:15:21 +03:00
private void DefaultDesugarPattern(expression matchingExpression, pattern_case patternCase)
2017-02-20 13:57:13 +03:00
{
//var paramCheckExpr = DesugarDeconstructorPatternParameters(patternCase.pattern as deconstructor_pattern);
var isExpression = new is_pattern_expr(matchingExpression, patternCase.pattern, patternCase.source_context);
var ifCondition = patternCase.condition == null ? (expression)isExpression : bin_expr.LogicalAnd(isExpression, patternCase.condition);
var ifCheck = SubtreeCreator.CreateIf(ifCondition, patternCase.case_action);
2018-05-15 17:55:57 +03:00
// Добавляем полученные statements в результат
AddDesugaredCaseToResult(ifCheck, ifCheck);
2018-05-01 19:29:39 +03:00
}
private type_definition GetTypeDefinitionForConstParam(expression constParamExpr)
{
/*switch (constParamExpr)
{
case string_const type:
return new named_type_reference("string");
case char_const type:
return new named_type_reference("char");
case int32_const type:
return new named_type_reference("integer");
case int64_const type:
return new named_type_reference("integer");
case double_const type:
return new named_type_reference("double");
}*/
if (constParamExpr is string_const)
return new named_type_reference("string");
if (constParamExpr is char_const)
return new named_type_reference("char");
if (constParamExpr is int32_const)
return new named_type_reference("integer");
if (constParamExpr is int64_const)
return new named_type_reference("integer");
if (constParamExpr is double_const)
return new named_type_reference("double");
return null;
}
private expression DesugarDeconstructorPatternParameters(deconstructor_pattern pattern)
{
expression paramCheckExpr = null;
for (int i = 0; i < pattern.parameters.Count; ++i)
{
if (pattern.parameters[i] is const_pattern_parameter constPattern)
{
var constParamIdent = new ident(NewDeconstructParamId(), pattern.parameters[i].source_context);
var eqParams = new expression_list(
new List<expression>()
{
constPattern.const_param,
constParamIdent
}
);
var constParamCheck = new method_call(
new dot_node(new ident("object"), new ident("Equals")),
eqParams,
pattern.source_context
);
pattern.parameters[i] = new var_deconstructor_parameter(
constParamIdent,
GetTypeDefinitionForConstParam(constPattern.const_param),
false,
pattern.parameters[i].source_context);
paramCheckExpr = paramCheckExpr == null ? (expression)constParamCheck : bin_expr.LogicalAnd(paramCheckExpr, constParamCheck);
}
if (pattern.parameters[i] is wild_card_deconstructor_parameter)
{
var wildCardGeneratedParamIdent = new ident(NewDeconstructParamId(), pattern.parameters[i].source_context);
pattern.parameters[i] = new var_deconstructor_parameter(
wildCardGeneratedParamIdent,
null,
false,
pattern.parameters[i].source_context);
}
if (pattern.parameters[i] is recursive_deconstructor_parameter deconstructor_param)
{
if (deconstructor_param.pattern is deconstructor_pattern deconstructor_pattern)
{
var recursiveChecks = DesugarDeconstructorPatternParameters(deconstructor_pattern);
paramCheckExpr = paramCheckExpr == null ?
recursiveChecks :
bin_expr.LogicalAnd(paramCheckExpr, recursiveChecks);
}
}
}
return paramCheckExpr;
}
private void DesugarConstPatternCase(expression matchingExpression, pattern_case patternCase)
{
Debug.Assert(patternCase.pattern is const_pattern);
var patternExpressionNode = patternCase.pattern as const_pattern;
var statementsToAdd = new List<statement>();
2019-05-25 12:30:15 +03:00
var equalCalls = new List<bin_expr>();
foreach (var patternExpression in patternExpressionNode.pattern_expressions.expressions)
{
statementsToAdd.Add(GetTypeCompatibilityCheck(matchingExpression, patternExpression));
2019-05-25 12:30:15 +03:00
equalCalls.Add(
2019-05-25 12:30:15 +03:00
new bin_expr(matchingExpression, patternExpression, Operators.Equal, patternCase.source_context
)
);
}
typeChecks.AddRange(statementsToAdd);
expression orPatternCases = equalCalls[0];
for (int i = 1; i < equalCalls.Count; ++i)
{
orPatternCases = bin_expr.LogicalOr(orPatternCases, equalCalls[i]);
}
var ifCondition = patternCase.condition == null ? orPatternCases : bin_expr.LogicalAnd(orPatternCases, patternCase.condition);
var ifCheck = SubtreeCreator.CreateIf(ifCondition, patternCase.case_action);
// Добавляем полученные statements в результат
AddDesugaredCaseToResult(ifCheck, ifCheck);
}
private expression GetCollectionItemsEqualCheckBeforeGap(addressed_value matchingExpression,
List<pattern_parameter> toCompare,
CollectionDesugaringResult desugaringResult)
{
var fromIndex = 0;
expression equalChecks = null;
foreach (var param in toCompare)
{
if (param is const_pattern_parameter constParam)
{
var indexerCall = new indexer(
matchingExpression,
new expression_list(
new int32_const(fromIndex, matchingExpression.source_context),
matchingExpression.source_context),
matchingExpression.source_context);
var eqParams = new expression_list(
new List<expression>()
{
indexerCall,
constParam.const_param
}
);
var equalCall = new method_call(
new dot_node(
new ident("object"),
new ident("Equals")),
eqParams,
matchingExpression.source_context
);
equalChecks = equalChecks == null ? (expression)equalCall : bin_expr.LogicalAnd(equalChecks, equalCall);
2019-05-25 12:30:15 +03:00
desugaringResult.ElemTypeChecks.Add(GetTypeCompatibilityCheck(indexerCall, constParam.const_param));
}
if (param is collection_pattern_var_parameter varParam)
{
desugaringResult.VarParametersDeclarations.Add(
new var_statement(varParam.identifier,
GetIndexerCallForCollectionPattern(matchingExpression as addressed_value, fromIndex)));
}
++fromIndex;
}
return equalChecks;
}
private expression GetCollectionItemsEqualCheckAfterGap(addressed_value matchingExpression,
List<pattern_parameter> toCompare,
CollectionDesugaringResult desugaringResult)
{
var elemFromTail = 1;
expression equalChecks = null;
foreach (var param in toCompare)
{
var indexerCall = new indexer(
matchingExpression,
new expression_list(
new bin_expr(
new method_call(
new dot_node(
matchingExpression,
new ident("Count", matchingExpression.source_context)),
new expression_list()),
new int32_const(elemFromTail, matchingExpression.source_context),
Operators.Minus),
matchingExpression.source_context),
matchingExpression.source_context);
if (param is const_pattern_parameter constParam)
{
var eqParams = new expression_list(
new List<expression>()
{
indexerCall,
constParam.const_param
}
);
var equalCall = new method_call(
new dot_node(
new ident("object"),
new ident("Equals")),
eqParams,
matchingExpression.source_context
);
equalChecks = equalChecks == null ? (expression)equalCall : bin_expr.LogicalAnd(equalChecks, equalCall);
2019-05-25 12:30:15 +03:00
desugaringResult.ElemTypeChecks.Add(GetTypeCompatibilityCheck(indexerCall, constParam.const_param));
}
if (param is collection_pattern_var_parameter varParam)
{
desugaringResult.VarParametersDeclarations.Add(
new var_statement(varParam.identifier, indexerCall));
}
++elemFromTail;
}
return equalChecks;
}
private expression GetIndexerCallForCollectionPattern(addressed_value matchingExpression, int ind)
{
var indexerCall = new indexer(matchingExpression, new int32_const(ind), matchingExpression.source_context);
return indexerCall;
}
2018-05-01 19:29:39 +03:00
private ident NewGeneralName(SourceContext sc) => new ident(generatedNamesManager.GenerateName(GeneratedPatternNamePrefix + "GenVar"), sc);
2017-02-20 13:57:13 +03:00
private ident NewSuccessName(SourceContext sc) => new ident(generatedNamesManager.GenerateName(GeneratedPatternNamePrefix + "Success"), sc);
2018-05-16 00:26:07 +03:00
// private ident NewEndIfName(SourceContext sc) => new ident(GeneratedPatternNamePrefix + "EndIf" + labelVariableCounter++, sc);
// private bool IsGenerated(string name) => name.StartsWith(GeneratedPatternNamePrefix);
2018-05-15 17:55:57 +03:00
2018-05-11 19:26:55 +03:00
private void AddDefaultCase(statement_list statements)
{
AddDesugaredCaseToResult(statements, _previousIf);
}
private void AddDesugaredCaseToResult(statement desugaredCase, if_node newIf)
{
// Если результат пустой, значит это первый case
if (desugaredMatchWith == null)
desugaredMatchWith = desugaredCase;
else
2018-05-11 19:26:55 +03:00
{
_previousIf.else_body = desugaredCase;
2018-05-11 19:26:55 +03:00
_previousIf.FillParentsInDirectChilds();
}
// Запоминаем только что сгенерированный if
_previousIf = newIf;
}
2018-05-06 20:09:36 +03:00
private DeconstructionDesugaringResult DesugarDeconstructorPattern(deconstructor_pattern pattern, expression matchingExpression)
2018-05-06 20:09:36 +03:00
{
2018-05-15 17:55:57 +03:00
Debug.Assert(!pattern.IsRecursive, "All recursive patterns should be desugared into simple patterns at this point");
2018-05-11 13:46:59 +03:00
2018-05-15 17:55:57 +03:00
var desugarResult = new DeconstructionDesugaringResult();
2020-06-18 08:23:11 +03:00
var castVariableName = NewGeneralName(pattern.source_context);
desugarResult.CastVariableDefinition = new var_statement(castVariableName, pattern.type, pattern.type.source_context);
2018-05-06 20:09:36 +03:00
2020-06-18 08:23:11 +03:00
var successVariableName = NewSuccessName(matchingExpression.source_context);
desugarResult.SuccessVariableDefinition = new var_statement(successVariableName, new ident("false"), successVariableName.source_context);
2018-05-06 20:09:36 +03:00
2018-05-15 17:55:57 +03:00
// делегирование проверки паттерна функции IsTest
desugarResult.TypeCastCheck = SubtreeCreator.CreateSystemFunctionCall(IsTestMethodName, matchingExpression, castVariableName);
2018-05-15 17:55:57 +03:00
var parameters = pattern.parameters.Cast<var_deconstructor_parameter>();
foreach (var deconstructedVariable in parameters)
{
2018-05-16 00:26:07 +03:00
desugarResult.DeconstructionVariables.Add(
2020-06-18 08:23:11 +03:00
new var_def_statement(deconstructedVariable.identifier, deconstructedVariable.type, deconstructedVariable.identifier.source_context));
2018-05-15 17:55:57 +03:00
}
2018-05-15 17:55:57 +03:00
var deconstructCall = new procedure_call();
deconstructCall.func_name = SubtreeCreator.CreateMethodCallWithQualifier(DeconstructMethodName, castVariableName.name, parameters.Select(x => x.identifier).ToArray());
2018-05-15 17:55:57 +03:00
desugarResult.DeconstructCall = deconstructCall;
return desugarResult;
}
2018-05-06 20:09:36 +03:00
private CollectionDesugaringResult DesugarCollectionPattern(collection_pattern pattern, expression matchingExpression)
{
Debug.Assert(!pattern.IsRecursive, "All recursive patterns should be desugared into simple patterns at this point");
2018-05-15 17:55:57 +03:00
var desugaringResult = new CollectionDesugaringResult();
var collectionItems = pattern.parameters;
var gapItemMet = false;
var gapIndex = 0;
var exprBeforeGap = new List<pattern_parameter>();
var exprAfterGap = new List<pattern_parameter>();
for (int i = 0; i < collectionItems.Count; ++i)
{
if (collectionItems[i] is collection_pattern_gap_parameter)
{
if (gapItemMet)
{
throw new SyntaxVisitorError("REPEATED_DOTDOT_COLLECTION_PATTERN_EXPR",
pattern.source_context);
}
gapItemMet = true;
gapIndex = i;
continue;
}
if (gapItemMet)
{
exprAfterGap.Insert(0, collectionItems[i]);
}
else
{
exprBeforeGap.Add(collectionItems[i]);
}
}
var successMatchingCheck = GetCollectionItemsEqualCheckBeforeGap(
matchingExpression as addressed_value, exprBeforeGap, desugaringResult);
if (gapItemMet && exprAfterGap.Count != 0)
{
var afterGapEqual = GetCollectionItemsEqualCheckAfterGap(
matchingExpression as addressed_value, exprAfterGap, desugaringResult);
if (afterGapEqual != null)
{
successMatchingCheck = successMatchingCheck == null ?
afterGapEqual :
bin_expr.LogicalAnd(successMatchingCheck, afterGapEqual);
}
}
desugaringResult.CollectionLengthCheck = new bin_expr(
new dot_node(matchingExpression as addressed_value, new ident(CountPropertyName), pattern.source_context),
new int32_const(exprBeforeGap.Count + exprAfterGap.Count),
Operators.GreaterEqual,
pattern.source_context
);
2018-05-17 00:01:52 +03:00
if (!gapItemMet)
{
var lengthWithoutGapCheck = new bin_expr(
new dot_node(matchingExpression as addressed_value, new ident(CountPropertyName), pattern.source_context),
new int32_const(exprBeforeGap.Count),
Operators.Equal,
pattern.source_context
);
successMatchingCheck = successMatchingCheck == null ?
lengthWithoutGapCheck :
bin_expr.LogicalAnd(lengthWithoutGapCheck, successMatchingCheck);
}
desugaringResult.SuccessMatchingCheck = successMatchingCheck == null ?
new bool_const(true) :
successMatchingCheck;
return desugaringResult;
}
private TupleDesugaringResult DesugarTuplePattern(tuple_pattern pattern, expression matchingExpression)
{
Debug.Assert(!pattern.IsRecursive, "All recursive patterns should be desugared into simple patterns at this point");
var desugaringResult = new TupleDesugaringResult();
var tupleItems = pattern.parameters;
for (int i = 0; i < tupleItems.Count; ++i)
2018-05-17 00:01:52 +03:00
{
var tupleItemCall = new dot_node(
matchingExpression as addressed_value,
2020-06-24 09:41:52 +03:00
new ident("Item" + (i + 1).ToString(), matchingExpression.source_context), // SSM 24/06/20
matchingExpression.source_context);
if (tupleItems[i] is tuple_pattern_var_parameter varParam)
{
desugaringResult.VarParametersDeclarations.Add(
new var_statement(
varParam.identifier,
tupleItemCall,
matchingExpression.source_context
)
);
}
if (tupleItems[i] is const_pattern_parameter constParam)
{
var eqParams = new expression_list(
new List<expression>()
{
tupleItemCall,
constParam.const_param
}
);
var equalCall = new method_call(
new dot_node(new ident("object"), new ident("Equals")),
eqParams,
pattern.source_context
);
desugaringResult.SuccessMatchingCheck = desugaringResult.SuccessMatchingCheck == null ?
(expression)equalCall :
bin_expr.LogicalAnd(desugaringResult.SuccessMatchingCheck, equalCall);
2019-05-25 12:30:15 +03:00
desugaringResult.ElemTypeChecks.Add(GetTypeCompatibilityCheck(tupleItemCall, constParam.const_param));
}
}
desugaringResult.TupleLengthCheck = GetTypeCompatibilityCheck(matchingExpression, new int32_const(tupleItems.Count));
if (desugaringResult.SuccessMatchingCheck == null)
{
desugaringResult.SuccessMatchingCheck = new bool_const(true);
}
return desugaringResult;
}
private void DesugarIsExpression(is_pattern_expr isPatternExpr)
{
if (isPatternExpr.right.IsRecursive)
{
var desugaredRecursiveIs = DesugarRecursiveParameters(isPatternExpr.left, isPatternExpr.right);
2018-05-17 00:01:52 +03:00
ReplaceUsingParent(isPatternExpr, desugaredRecursiveIs);
desugaredRecursiveIs.visit(this);
return;
}
var patternLocation = GetLocation(isPatternExpr);
2018-05-16 00:26:07 +03:00
if (isPatternExpr.right is deconstructor_pattern pattern)
{
var constParamCheck = DesugarDeconstructorPatternParameters(isPatternExpr.right as deconstructor_pattern);
pattern.const_params_check = constParamCheck;
}
//AddDefinitionsInUpperStatementList(isPatternExpr, new[] { GetTypeCompatibilityCheck(isPatternExpr) });
2018-05-16 00:26:07 +03:00
switch (patternLocation)
{
case PatternLocation.IfCondition: DesugarIsExpressionInIfCondition(isPatternExpr); break;
case PatternLocation.Assign: DesugarIsExpressionInAssignment(isPatternExpr); break;
}
}
2018-05-15 17:55:57 +03:00
private expression DesugarRecursiveParameters(expression expression, pattern_node pattern)
2018-05-17 00:01:52 +03:00
{
List<pattern_parameter> parameters = pattern.parameters;
expression conjunction = new is_pattern_expr(expression, pattern, pattern.source_context);
2018-05-17 00:01:52 +03:00
for (int i = 0; i < parameters.Count; i++)
{
if (parameters[i] is recursive_pattern_parameter parameter)
2018-05-17 00:01:52 +03:00
{
2018-05-23 17:56:19 +03:00
//var parameterType = (parameter.pattern as deconstructor_pattern).type;
2020-06-18 08:23:11 +03:00
var newName = NewGeneralName(parameter.source_context);
pattern_parameter varParameter = null;
if (pattern is deconstructor_pattern)
{
varParameter = new var_deconstructor_parameter(newName, null, false);
}
else if (pattern is collection_pattern)
{
varParameter = new collection_pattern_var_parameter(newName, null);
}
else if (pattern is tuple_pattern)
{
varParameter = new tuple_pattern_var_parameter(newName, null);
}
2018-05-17 00:01:52 +03:00
parameters[i] = varParameter;
varParameter.Parent = parameters[i];
conjunction = bin_expr.LogicalAnd(conjunction, DesugarRecursiveParameters(newName, parameter.pattern));
2018-05-17 00:01:52 +03:00
}
}
return conjunction;
}
2018-05-16 00:26:07 +03:00
private void DesugarIsExpressionInAssignment(is_pattern_expr isExpression)
{
if (!(isExpression.right is deconstructor_pattern))
{
throw new SyntaxVisitorError("MATCHING_ASSIGN_NOT_SUPPORTED", isExpression.right.source_context);
}
var statementsToAdd = ProcessDesugaringForDeconstructorPattern(isExpression);
AddDefinitionsInUpperStatementList(isExpression, statementsToAdd);
2018-05-16 00:26:07 +03:00
}
2018-05-15 17:55:57 +03:00
2018-05-16 00:26:07 +03:00
private void DesugarIsExpressionInIfCondition(is_pattern_expr isExpression)
{
List<statement> statementsToAdd = null;
2018-05-15 17:55:57 +03:00
/*switch (isExpression.right)
{
case deconstructor_pattern dp:
if (dp.const_params_check != null)
{
var ifToAddConstParamsCheckTo = GetAscendant<if_node>(isExpression);
ifToAddConstParamsCheckTo.condition =
bin_expr.LogicalAnd(ifToAddConstParamsCheckTo.condition, dp.const_params_check);
}
statementsToAdd = ProcessDesugaringForDeconstructorPattern(isExpression);
break;
case collection_pattern cp:
statementsToAdd = ProcessDesugaringForCollectionPattern(isExpression);
break;
case tuple_pattern cp:
statementsToAdd = ProcessDesugaringForTuplePattern(isExpression);
break;
2020-02-10 15:51:16 +03:00
case const_pattern cp:
statementsToAdd = ProcessDesugaringForConstPattern(isExpression);
break;
}*/
if (isExpression.right is deconstructor_pattern dp)
{
if (dp.const_params_check != null)
{
var ifToAddConstParamsCheckTo = GetAscendant<if_node>(isExpression);
ifToAddConstParamsCheckTo.condition =
bin_expr.LogicalAnd(ifToAddConstParamsCheckTo.condition, dp.const_params_check);
}
statementsToAdd = ProcessDesugaringForDeconstructorPattern(isExpression);
}
else if (isExpression.right is collection_pattern)
{
statementsToAdd = ProcessDesugaringForCollectionPattern(isExpression);
}
else if (isExpression.right is tuple_pattern)
{
statementsToAdd = ProcessDesugaringForTuplePattern(isExpression);
}
else if (isExpression.right is const_pattern)
{
statementsToAdd = ProcessDesugaringForConstPattern(isExpression);
}
2018-05-16 00:26:07 +03:00
var enclosingIf = GetAscendant<if_node>(isExpression);
2018-05-16 00:26:07 +03:00
// Если уже обрабатывался ранее (второй встретившийся в том же условии is), то не изменяем if
if (processedIfNodes.Contains(enclosingIf))
AddDefinitionsInUpperStatementList(isExpression, statementsToAdd);
2018-05-16 00:26:07 +03:00
// Иначе помещаем определения и if-then в отдельный блок, а else после этого блока
else
{
// Сохраняем родителя, так как он поменяется при вызове ConvertIfNode
var ifParent = enclosingIf.Parent;
2018-05-17 02:03:53 +03:00
statement elseBody;
var convertedIf = ConvertIfNode(enclosingIf, statementsToAdd, out elseBody);
ifParent.ReplaceDescendantUnsafe(enclosingIf, convertedIf);
convertedIf.Parent = ifParent;
2018-05-17 02:03:53 +03:00
elseBody?.visit(this);
}
2018-05-16 00:26:07 +03:00
}
2020-02-10 15:51:16 +03:00
private List<statement> ProcessDesugaringForConstPattern(is_pattern_expr isExpression)
{
var patternExpressionNode = isExpression.right as const_pattern;
var statementsToAdd = new List<statement>();
var equalCalls = new List<bin_expr>();
foreach (var patternExpression in patternExpressionNode.pattern_expressions.expressions)
{
statementsToAdd.Add(GetTypeCompatibilityCheck(isExpression.left, patternExpression));
equalCalls.Add(
new bin_expr(isExpression.left, patternExpression, Operators.Equal, isExpression.source_context
)
);
}
typeChecks.AddRange(statementsToAdd);
expression orPatternCases = equalCalls[0];
for (int i = 1; i < equalCalls.Count; ++i)
{
orPatternCases = bin_expr.LogicalOr(orPatternCases, equalCalls[i]);
}
ReplaceUsingParent(isExpression, orPatternCases);
return statementsToAdd;
}
private List<statement> ProcessDesugaringForDeconstructorPattern(is_pattern_expr isExpression)
{
var pattern = isExpression.right as deconstructor_pattern;
var desugaringResult = DesugarDeconstructorPattern(pattern, isExpression.left);
ReplaceUsingParent(isExpression, desugaringResult.SuccessVariable);
var statementsToAdd = desugaringResult.GetDeconstructionDefinitions(pattern.source_context);
statementsToAdd.Add(GetMatchedExpressionCheck(isExpression.left));
statementsToAdd.Add(GetTypeCompatibilityCheck(isExpression));
statementsToAdd.Add(desugaringResult.GetPatternCheckWithDeconstrunctorCall());
return statementsToAdd;
}
private List<statement> ProcessDesugaringForCollectionPattern(is_pattern_expr isExpression)
{
var pattern = isExpression.right as collection_pattern;
var desugaringResult = DesugarCollectionPattern(pattern, isExpression.left);
ReplaceUsingParent(isExpression, desugaringResult.SuccessMatchingCheck);
var statementsToAdd = new List<statement>();
statementsToAdd.AddRange(desugaringResult.VarParametersDeclarations);
statementsToAdd.AddRange(desugaringResult.ElemTypeChecks);
return statementsToAdd;
}
private List<statement> ProcessDesugaringForTuplePattern(is_pattern_expr isExpression)
{
var pattern = isExpression.right as tuple_pattern;
var desugaringResult = DesugarTuplePattern(pattern, isExpression.left);
ReplaceUsingParent(isExpression, desugaringResult.SuccessMatchingCheck);
var statementsToAdd = new List<statement>();
statementsToAdd.Add(desugaringResult.TupleLengthCheck);
statementsToAdd.AddRange(desugaringResult.VarParametersDeclarations);
statementsToAdd.AddRange(desugaringResult.ElemTypeChecks);
return statementsToAdd;
}
2018-06-17 14:21:52 +03:00
private semantic_check_sugared_statement_node GetMatchedExpressionCheck(expression matchedExpression)
2020-07-14 18:55:14 +03:00
{
return new semantic_check_sugared_statement_node(SemanticCheckType.MatchedExpression, new List<syntax_tree_node>() { matchedExpression });
}
2018-06-17 14:21:52 +03:00
2020-07-14 18:55:14 +03:00
private semantic_check_sugared_statement_node GetTypeCompatibilityCheck(is_pattern_expr expression)
{
return new semantic_check_sugared_statement_node(SemanticCheckType.MatchedExpressionAndType, new List<syntax_tree_node>() { expression.left, (expression.right as deconstructor_pattern).type });
}
2020-07-14 18:55:14 +03:00
private semantic_check_sugared_statement_node GetTypeCompatibilityCheck(expression expression1, expression expression2)
{
return new semantic_check_sugared_statement_node(SemanticCheckType.MatchedExpressionAndExpression, new List<syntax_tree_node>() { expression1, expression2 });
}
2020-07-14 18:55:14 +03:00
private semantic_check_sugared_statement_node GetTypeCompatibilityCheck(expression tuple, int32_const length)
{
return new semantic_check_sugared_statement_node(SemanticCheckType.MatchedTuple, new List<syntax_tree_node>() { tuple, length });
}
2019-06-15 12:15:21 +03:00
private bool IsNestedIfWithExtendedIs(if_node ifNode)
{
var parent = ifNode.Parent;
while (parent != null)
{
if (parent is statement_list stList &&
IsVisitElseBranchStatementListDeclaration(stList))
{
return true;
}
parent = parent.Parent;
}
return false;
}
private bool IsVisitElseBranchStatementListDeclaration(statement_list stList)
{
return stList.list != null && stList.list[0] != null &&
stList.list[0] is var_statement visitElseVarStatement &&
visitElseVarStatement.var_def.vars.idents[0].name.Equals(GeneratedVisitElseBranchVariableName);
}
2018-05-17 02:03:53 +03:00
private statement_list ConvertIfNode(if_node ifNode, List<statement> statementsBeforeIf, out statement elseBody)
2018-05-16 00:26:07 +03:00
{
// if e then <then> else <else>
//
// переводим в
//
// begin
2019-06-15 12:15:21 +03:00
// var <>visitElseBranch := true;
// begin
// <>visitElseBranch := true;
// statementsBeforeIf
// if e then begin <then>; <>visitElseBranch := false; end;
// end
// if <>visitElseBranch then <else>
2018-05-16 00:26:07 +03:00
// end
// if e then <then>
//
// переводим в
//
// begin
// statementsBeforeIf
// if e then <then>
// end
2019-06-15 12:15:21 +03:00
// Добавляем объявление <>visitElseBranch если мы находимся в первом в цепочке if, который не является вложенным
List<statement> visitElseStatList = null;
if (ifNode.else_body != null &&
!(ifNode.Parent is if_node ifParentNode &&
ifParentNode.condition is ident ifParentNodeIdent &&
ifParentNodeIdent.name.Equals(GeneratedVisitElseBranchVariableName)) &&
!IsNestedIfWithExtendedIs(ifNode))
{
visitElseStatList = new List<statement>();
visitElseStatList.Add(
new var_statement(
new ident(GeneratedVisitElseBranchVariableName, ifNode.source_context),
new bool_const(true, ifNode.source_context),
ifNode.source_context)
);
}
2018-05-16 00:26:07 +03:00
// Добавляем, чтобы на конвертировать еще раз, если потребуется
processedIfNodes.Add(ifNode);
2019-06-15 12:15:21 +03:00
if (ifNode.else_body != null)
{
statementsBeforeIf.Add(new assign(
new ident(GeneratedVisitElseBranchVariableName, ifNode.source_context),
new bool_const(true, ifNode.source_context),
Operators.Assignment,
ifNode.source_context));
}
2018-05-16 00:26:07 +03:00
var statementsBeforeAndIf = new statement_list();
statementsBeforeAndIf.AddMany(statementsBeforeIf);
statementsBeforeAndIf.Add(ifNode);
if (ifNode.else_body == null)
2018-05-17 02:03:53 +03:00
{
elseBody = null;
2019-06-15 12:15:21 +03:00
if (visitElseStatList != null)
{
visitElseStatList.Add(statementsBeforeAndIf);
statementsBeforeAndIf = new statement_list(visitElseStatList);
}
2018-05-16 00:26:07 +03:00
return statementsBeforeAndIf;
2018-05-17 02:03:53 +03:00
}
2018-05-16 00:26:07 +03:00
else
{
var result = new statement_list();
result.Add(statementsBeforeAndIf);
2020-06-18 08:23:11 +03:00
//var endIfLabel = NewEndIfName();
2019-06-15 12:15:21 +03:00
2018-05-16 00:26:07 +03:00
if (!(ifNode.then_body is statement_list))
{
ifNode.then_body = new statement_list(ifNode.then_body, ifNode.then_body.source_context);
ifNode.then_body.Parent = ifNode;
}
var thenBody = ifNode.then_body as statement_list;
2019-06-15 12:15:21 +03:00
thenBody.Add(new assign(
new ident(GeneratedVisitElseBranchVariableName, thenBody.source_context),
new bool_const(false, thenBody.source_context),
Operators.Assignment,
thenBody.source_context));
// добавляем else
result.Add(
new if_node(
new ident(GeneratedVisitElseBranchVariableName, ifNode.else_body.source_context),
ifNode.else_body,
null,
ifNode.else_body.source_context));
2018-05-17 02:03:53 +03:00
// Возвращаем else для обхода, т.к. он уже не входит в if
elseBody = ifNode.else_body;
2018-05-16 00:26:07 +03:00
// удаляем else из if
ifNode.else_body = null;
2019-06-15 12:15:21 +03:00
if (visitElseStatList != null)
{
visitElseStatList.Add(result);
result = new statement_list(visitElseStatList);
}
2018-05-16 00:26:07 +03:00
return result;
}
}
private void AddLabel(ident label)
{
2018-05-17 15:20:16 +03:00
var block = listNodes.OfType<block>().Last();
2018-05-16 00:26:07 +03:00
if (block.defs == null)
block.defs = new declarations();
2018-05-17 15:20:16 +03:00
block.defs.AddFirst(new label_definitions(label));
2018-05-15 17:55:57 +03:00
}
private void AddDefinitionsInUpperStatementList(syntax_tree_node currentNode, IEnumerable<statement> statementsToAdd)
2018-05-15 17:55:57 +03:00
{
var definitionsAdded = false;
var ascendants = currentNode.AscendantNodes(true).ToArray();
// Объявление переменной в ближайшем statement_list
for (int i = 0; i < ascendants.Length; i++)
{
if (ascendants[i] is statement_list statements)
2018-05-15 17:55:57 +03:00
{
statements.InsertBefore(
ascendants[i - 1] as statement,
2018-05-15 17:55:57 +03:00
statementsToAdd);
2018-05-15 17:55:57 +03:00
definitionsAdded = true;
break;
}
}
2018-05-15 17:55:57 +03:00
Debug.Assert(definitionsAdded, "Couldn't add definitions");
}
private PatternLocation GetLocation(syntax_tree_node node)
2018-05-15 17:55:57 +03:00
{
var firstStatement = GetAscendant<statement>(node);
/*switch (firstStatement)
2018-05-16 00:26:07 +03:00
{
case if_node _: return PatternLocation.IfCondition;
case var_statement _: return PatternLocation.Assign;
case assign _: return PatternLocation.Assign;
2018-05-16 00:26:07 +03:00
default: return PatternLocation.Unknown;
}*/
if (firstStatement is if_node)
return PatternLocation.IfCondition;
if (firstStatement is var_statement)
return PatternLocation.Assign;
if (firstStatement is assign)
return PatternLocation.Assign;
return PatternLocation.Unknown;
2018-05-15 17:55:57 +03:00
}
private T GetAscendant<T>(syntax_tree_node node)
2018-05-15 17:55:57 +03:00
where T : syntax_tree_node
{
var current = node.Parent;
while (current != null)
2018-05-15 17:55:57 +03:00
{
if (current is T res)
return res;
current = current.Parent;
2018-05-15 17:55:57 +03:00
}
return null;
2018-05-06 20:09:36 +03:00
}
private string NewDeconstructParamId()
{
return generatedNamesManager.GenerateName(GeneratedPatternNamePrefix + "DeconstructParam");
}
private string NewMatchExprVariableId()
{
return generatedNamesManager.GenerateName(GeneratedMatchExprVariableName + "matchExprVariableCounter");
}
2017-02-20 13:57:13 +03:00
}
}
2018-05-11 13:46:59 +03:00