pascalabcnet/SyntaxVisitors/SugarVisitors/PatternsDesugaringVisitor.cs

407 lines
17 KiB
C#
Raw Permalink 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;
using PascalABCCompiler.TreeConverter;
2017-02-20 13:57:13 +03:00
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; }
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);
2018-05-17 00:01:52 +03:00
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 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 = compiler_string_consts.deconstruct_method_name;
2018-05-15 17:55:57 +03:00
private const string IsTestMethodName = compiler_string_consts.is_test_function_name;
private const string GeneratedPatternNamePrefix = "<>pattern";
2018-05-06 20:09:36 +03:00
2018-05-17 22:49:07 +03:00
private int generalVariableCounter = 0;
private int successVariableCounter = 0;
private int labelVariableCounter = 0;
private if_node _previousIf;
2018-05-11 19:26:55 +03:00
private statement desugaredMatchWith;
2017-02-20 13:57:13 +03:00
2018-05-16 00:26:07 +03:00
private List<if_node> processedIfNodes = new List<if_node>();
public static PatternsDesugaringVisitor New => new PatternsDesugaringVisitor();
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 cachedExpression = NewGeneralName();
//AddDefinitionsInUpperStatementList(matchWith, new[] { new var_statement(cachedExpression, matchWith.expr) });
// Преобразование из сахара в известную конструкцию каждого 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)
DesugarDeconstructorPatternCase(matchWith.expr, patternCase);
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
// Замена выражения match with на новое несахарное поддерево и его обход
ReplaceUsingParent(matchWith, desugaredMatchWith);
visit(desugaredMatchWith);
2017-02-20 13:57:13 +03:00
}
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");
2018-05-17 00:01:52 +03:00
2018-05-16 00:26:07 +03:00
DesugarIsExpression(isPatternExpr);
}
2018-05-15 17:55:57 +03:00
void DesugarDeconstructorPatternCase(expression matchingExpression, pattern_case patternCase)
2017-02-20 13:57:13 +03:00
{
2018-05-15 17:55:57 +03:00
Debug.Assert(patternCase.pattern is deconstructor_pattern);
var isExpression = new is_pattern_expr(matchingExpression, patternCase.pattern);
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
}
2018-05-17 22:49:07 +03:00
private ident NewGeneralName() => new ident(GeneratedPatternNamePrefix + "GenVar" + generalVariableCounter++);
2017-02-20 13:57:13 +03:00
private ident NewSuccessName() => new ident(GeneratedPatternNamePrefix + "Success" + successVariableCounter++);
2018-05-16 00:26:07 +03:00
private ident NewEndIfName() => new ident(GeneratedPatternNamePrefix + "EndIf" + labelVariableCounter++);
2018-05-15 17:55:57 +03:00
private bool IsGenerated(string name) => name.StartsWith(GeneratedPatternNamePrefix);
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 DesugarPattern(deconstructor_pattern pattern, expression matchingExpression)
{
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();
var castVariableName = NewGeneralName();
desugarResult.CastVariableDefinition = new var_statement(castVariableName, pattern.type);
2018-05-06 20:09:36 +03:00
2018-05-15 17:55:57 +03:00
var successVariableName = NewSuccessName();
desugarResult.SuccessVariableDefinition = new var_statement(successVariableName, new ident("false"));
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(
new var_def_statement(deconstructedVariable.identifier, deconstructedVariable.type));
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.CreateMethodCall(DeconstructMethodName, castVariableName.name, parameters.Select(x => x.identifier).ToArray());
desugarResult.DeconstructCall = deconstructCall;
return desugarResult;
}
2018-05-06 20:09:36 +03:00
2018-05-15 17:55:57 +03:00
private void DesugarIsExpression(is_pattern_expr isPatternExpr)
{
2018-05-15 17:55:57 +03:00
Debug.Assert(isPatternExpr.right is deconstructor_pattern);
var patternLocation = GetLocation(isPatternExpr);
2018-05-17 00:01:52 +03:00
var pattern = isPatternExpr.right as deconstructor_pattern;
//AddDefinitionsInUpperStatementList(isPatternExpr, new[] { GetTypeCompatibilityCheck(isPatternExpr) });
2018-05-17 00:01:52 +03:00
if (pattern.IsRecursive)
{
var desugaredRecursiveIs = DesugarRecursiveDeconstructor(isPatternExpr.left, pattern);
ReplaceUsingParent(isPatternExpr, desugaredRecursiveIs);
desugaredRecursiveIs.visit(this);
return;
}
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
2018-05-17 00:01:52 +03:00
private expression DesugarRecursiveDeconstructor(expression expression, deconstructor_pattern pattern)
{
List<pattern_deconstructor_parameter> parameters = pattern.parameters;
expression conjunction = new is_pattern_expr(expression, pattern);
for (int i = 0; i < parameters.Count; i++)
{
if (parameters[i] is recursive_deconstructor_parameter parameter)
{
2018-05-23 17:56:19 +03:00
//var parameterType = (parameter.pattern as deconstructor_pattern).type;
2018-05-17 00:01:52 +03:00
var newName = NewGeneralName();
2018-05-23 17:56:19 +03:00
var varParameter = new var_deconstructor_parameter(newName, null);
2018-05-17 00:01:52 +03:00
parameters[i] = varParameter;
varParameter.Parent = parameters[i];
conjunction = bin_expr.LogicalAnd(conjunction, DesugarRecursiveDeconstructor(newName, parameter.pattern as deconstructor_pattern));
}
}
return conjunction;
}
2018-05-16 00:26:07 +03:00
private void DesugarIsExpressionInAssignment(is_pattern_expr isExpression)
{
var pattern = isExpression.right as deconstructor_pattern;
var desugaringResult = DesugarPattern(pattern, isExpression.left);
ReplaceUsingParent(isExpression, desugaringResult.SuccessVariable);
2018-05-15 17:55:57 +03:00
var statementsToAdd = desugaringResult.GetDeconstructionDefinitions(pattern.source_context);
2018-06-17 14:21:52 +03:00
statementsToAdd.Add(GetMatchedExpressionCheck(isExpression.left));
statementsToAdd.Add(GetTypeCompatibilityCheck(isExpression));
2018-05-15 17:55:57 +03:00
statementsToAdd.Add(desugaringResult.GetPatternCheckWithDeconstrunctorCall());
2018-05-16 00:26:07 +03:00
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)
{
var pattern = isExpression.right as deconstructor_pattern;
var desugaringResult = DesugarPattern(pattern, isExpression.left);
ReplaceUsingParent(isExpression, desugaringResult.SuccessVariable);
2018-05-15 17:55:57 +03:00
2018-05-16 00:26:07 +03:00
var statementsToAdd = desugaringResult.GetDeconstructionDefinitions(pattern.source_context);
2018-06-17 14:21:52 +03:00
statementsToAdd.Add(GetMatchedExpressionCheck(isExpression.left));
statementsToAdd.Add(GetTypeCompatibilityCheck(isExpression));
2018-05-16 00:26:07 +03:00
statementsToAdd.Add(desugaringResult.GetPatternCheckWithDeconstrunctorCall());
var enclosingIf = GetAscendant<if_node>(isExpression);
2018-05-16 00:26:07 +03:00
// Если уже обрабатывался ранее (второй встретившийся в том же условии is), то не изменяем if
2018-06-17 14:21:52 +03:00
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
}
2018-06-17 14:21:52 +03:00
private semantic_check_sugared_statement_node GetMatchedExpressionCheck(expression matchedExpression)
=> new semantic_check_sugared_statement_node(SemanticCheckType.MatchedExpression, new List<syntax_tree_node>() { matchedExpression });
private semantic_check_sugared_statement_node GetTypeCompatibilityCheck(is_pattern_expr expression) =>
new semantic_check_sugared_statement_node(SemanticCheckType.MatchedExpressionAndType, new List<syntax_tree_node>() { expression.left, (expression.right as deconstructor_pattern).type });
2018-06-17 14:21:52 +03:00
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
// statementsBeforeIf
// if e then begin <then>; goto end_if end;
// end
// <else>
// end_if: empty_statement
// if e then <then>
//
// переводим в
//
// begin
// statementsBeforeIf
// if e then <then>
// end
// Добавляем, чтобы на конвертировать еще раз, если потребуется
processedIfNodes.Add(ifNode);
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;
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);
var endIfLabel = NewEndIfName();
// добавляем метку
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;
thenBody.Add(new goto_statement(endIfLabel));
// добавляем else и метку за ним
result.Add(ifNode.else_body);
result.Add(new labeled_statement(endIfLabel));
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;
// Добавляем метку
AddLabel(endIfLabel);
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);
2018-05-16 00:26:07 +03:00
switch (firstStatement)
{
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;
}
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
}
2017-02-20 13:57:13 +03:00
}
}
2018-05-11 13:46:59 +03:00