pascalabcnet/SyntaxVisitors/SugarVisitors/MatchWithVisitor.cs

177 lines
7.7 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 System.Text;
using PascalABCCompiler.SyntaxTree;
using PascalABCCompiler.TreeConversion;
namespace SyntaxVisitors.SugarVisitors
{
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; }
/// <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; }
public ident CastVariable
{
get
{
return CastVariableDefinition.var_def.vars.list[0];
}
}
}
2017-02-20 13:57:13 +03:00
public class MatchWithVisitor : BaseChangeVisitor
{
2018-05-06 20:09:36 +03:00
private const string DeconstructMethodName = "Deconstruct";
private int _variableCounter = 0;
private if_node _previousIf;
private statement_list desugaredMatchWith;
2017-02-20 13:57:13 +03:00
public static MatchWithVisitor New => new MatchWithVisitor();
public override void visit(match_with matchWith)
{
desugaredMatchWith = null;
_previousIf = null;
2017-02-20 13:57:13 +03:00
// Преобразование из сахара в известную конструкцию каждого case
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;
2018-05-01 19:29:39 +03:00
if (patternCase.pattern is deconstructor_pattern)
2018-05-06 20:09:36 +03:00
// TODO: introduce a variable for expression and cache it
2018-05-01 19:29:39 +03:00
DesugarDeconstructorPatternCase(matchWith.expr, patternCase);
2017-02-20 13:57:13 +03:00
}
2018-05-01 19:29:39 +03:00
if (desugaredMatchWith == null)
return;
// Замена выражения match with на новое несахарное поддерево и его обход
ReplaceUsingParent(matchWith, desugaredMatchWith);
visit(desugaredMatchWith);
2017-02-20 13:57:13 +03:00
}
void DesugarTypePatternCase(expression matchingExpression, pattern_case patternCase)
2017-02-20 13:57:13 +03:00
{
Debug.Assert(patternCase.pattern is type_pattern);
2018-05-06 20:09:36 +03:00
//var result = new statement_list();
//// создание фиктивной переменной необходимого типа
//var pattern = (type_pattern) patternCase.pattern;
//// Конструируем условие мэтчинга
//expression matchCondition = isTest;
//if (patternCase.condition == null)
// matchCondition = isTest;
//else
//// Если есть секция when - добавляем ее в условие
//{
// // Переименовываем идентификатор, т.к. мы сгенерировали внутреннее имя
// patternCase.condition.RenameIdentifierInDescendants(pattern.identifier.name, matchResultVariableName.name);
// matchCondition = new bin_expr(isTest, patternCase.condition, Operators.LogicalAND);
//}
//// Создание узла if с объявлением переменной из паттерна и присвоение ей значения
//// сгенерированной переменной
//var patternVarDef = new var_statement(pattern.identifier, matchResultVariableName);
//// Создание тела if из объявления переменной и соттветствующего тела case
//var ifCheck = SubtreeCreator.CreateIf(matchCondition, new statement_list(patternVarDef, patternCase.case_action));
//result.Add(ifCheck);
//// Добавляем полученные statements в результат
//AddDesugaredCaseToResult(result, ifCheck);
2018-05-01 19:29:39 +03:00
}
void DesugarDeconstructorPatternCase(expression matchingExpression, pattern_case patternCase)
{
Debug.Assert(patternCase.pattern is deconstructor_pattern);
var result = new statement_list();
2018-05-06 20:09:36 +03:00
var pattern = patternCase.pattern as deconstructor_pattern;
2018-05-01 19:29:39 +03:00
// создание фиктивной переменной необходимого типа
2018-05-06 20:09:36 +03:00
var desugaredPattern = DesugarPattern(pattern, matchingExpression);
result.Add(desugaredPattern.CastVariableDefinition);
2018-05-01 19:29:39 +03:00
2018-05-06 20:09:36 +03:00
if (patternCase.condition != null)
2018-05-01 19:29:39 +03:00
// Если есть секция when - добавляем ее в условие
{
2018-05-06 20:09:36 +03:00
// TODO: generate check for 'when'
2018-05-01 19:29:39 +03:00
}
2018-05-06 20:09:36 +03:00
// Создание тела if из объявлений переменных, вызова Deconstruct и соответствующего тела case
var ifBody = new statement_list();
2018-05-11 13:46:59 +03:00
ifBody.Add(new desugared_deconstruction(desugaredPattern.DeconstructionVariables, desugaredPattern.CastVariable, patternCase.pattern.source_context));
2018-05-06 20:09:36 +03:00
ifBody.Add(desugaredPattern.DeconstructCall);
ifBody.Add(patternCase.case_action);
var ifCheck = SubtreeCreator.CreateIf(desugaredPattern.TypeCastCheck, ifBody);
2018-05-11 13:46:59 +03:00
result.Add(ifCheck);
2017-02-20 13:57:13 +03:00
// Добавляем полученные statements в результат
AddDesugaredCaseToResult(result, ifCheck);
2017-02-20 13:57:13 +03:00
}
private ident GenerateIdent()
{
2018-05-11 13:46:59 +03:00
return new ident("<>patternGenVar" + _variableCounter++);
2017-02-20 13:57:13 +03:00
}
private void AddDesugaredCaseToResult(statement_list statements, if_node newIf)
{
// Если результат пустой, значит это первый case
if (desugaredMatchWith == null)
desugaredMatchWith = statements;
else
_previousIf.else_body = statements;
// Запоминаем только что сгенерированный if
_previousIf = newIf;
}
2018-05-06 20:09:36 +03:00
private DeconstructionDesugaringResult DesugarPattern(deconstructor_pattern pattern, expression matchingExpression)
{
var desugarResult = new DeconstructionDesugaringResult();
var castVariableName = GenerateIdent();
desugarResult.CastVariableDefinition = new var_statement(castVariableName, pattern.type);
2018-05-11 13:46:59 +03:00
2018-05-06 20:09:36 +03:00
// делегирование проверки паттерна функции IsTest
desugarResult.TypeCastCheck = SubtreeCreator.CreateSystemFunctionCall("IsTest", matchingExpression, castVariableName);
foreach (var deconstructedVariable in pattern.parameters)
desugarResult.DeconstructionVariables.Add(
new var_def_statement(deconstructedVariable.identifier, deconstructedVariable.type));
var deconstructCall = new procedure_call();
deconstructCall.func_name = SubtreeCreator.CreateMethodCall(DeconstructMethodName, castVariableName.name, pattern.parameters.Select(x => x.identifier).ToArray());
desugarResult.DeconstructCall = deconstructCall;
return desugarResult;
}
2017-02-20 13:57:13 +03:00
}
}
2018-05-11 13:46:59 +03:00