using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using PascalABCCompiler.SyntaxTree; using PascalABCCompiler.TreeConversion; using PascalABCCompiler.TreeConverter; namespace SyntaxVisitors.SugarVisitors { // Patterns public class DeconstructionDesugaringResult { /// /// Переменная, имеющая тип паттерна /// public var_statement CastVariableDefinition { get; set; } /// /// Переменная, в которую сохраняется результат мэтчинга /// public var_statement SuccessVariableDefinition { get; set; } /// /// Переменные, полученные в результате деконструкции /// public List DeconstructionVariables { get; private set; } = new List(); /// /// Проверка соответствия типа выражения типу паттерна /// public expression TypeCastCheck { get; set; } /// /// Вызов Deconstruct /// public statement DeconstructCall { get; set; } public ident CastVariable => CastVariableDefinition.var_def.vars.list.First(); public ident SuccessVariable => SuccessVariableDefinition.var_def.vars.list.First(); public List GetDeconstructionDefinitions(SourceContext patternContext) { var result = new List(); result.Add(CastVariableDefinition); result.Add(new desugared_deconstruction(DeconstructionVariables, CastVariable, patternContext)); result.Add(SuccessVariableDefinition); return result; } public if_node GetPatternCheckWithDeconstrunctorCall() { return SubtreeCreator.CreateIf( TypeCastCheck, new statement_list(new assign(SuccessVariable.name, true), DeconstructCall)); } } public class PatternsDesugaringVisitor : BaseChangeVisitor { private enum PatternLocation { Unknown, IfCondition, Assign } private const string DeconstructMethodName = compiler_string_consts.deconstruct_method_name; private const string IsTestMethodName = compiler_string_consts.is_test_function_name; private const string GeneratedPatternNamePrefix = "<>pattern"; private int generalVariableCounter = 0; private int successVariableCounter = 0; private int labelVariableCounter = 0; private if_node _previousIf; private statement desugaredMatchWith; private List processedIfNodes = new List(); public static PatternsDesugaringVisitor New => new PatternsDesugaringVisitor(); public override void visit(match_with matchWith) { desugaredMatchWith = null; _previousIf = null; // Кэшируем выражение для однократного вычисления //var cachedExpression = NewGeneralName(); //AddDefinitionsInUpperStatementList(matchWith, new[] { new var_statement(cachedExpression, matchWith.expr) }); // Преобразование из сахара в известную конструкцию каждого case var usedDeconstructionTypes = new HashSet(); foreach (var patternCase in matchWith.case_list.elements) { if (patternCase == null) continue; if (patternCase.pattern is deconstructor_pattern) { // Проверяем встречался ли уже такой тип при деконструкции // SSM 02.01.19 пока закомментировал этот кусок т.к. при этом коде падает стандартный пример ArithmSimplify.cs. #1408 снова открыл /*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); } */ DesugarDeconstructorPatternCase(matchWith.expr, patternCase); } } if (matchWith.defaultAction != null) AddDefaultCase(matchWith.defaultAction as statement_list); if (desugaredMatchWith == null) desugaredMatchWith = new empty_statement(); // Замена выражения match with на новое несахарное поддерево и его обход ReplaceUsingParent(matchWith, desugaredMatchWith); visit(desugaredMatchWith); } public override void visit(is_pattern_expr isPatternExpr) { if (GetLocation(isPatternExpr) == PatternLocation.Unknown) throw new SyntaxVisitorError("PATTERN_MATHING_IS_NOT_SUPPORTED_IN_THIS_CONTEXT", isPatternExpr.source_context); Debug.Assert(GetAscendant(isPatternExpr) != null, "Couldn't find statement list in upper nodes"); DesugarIsExpression(isPatternExpr); } void DesugarDeconstructorPatternCase(expression matchingExpression, pattern_case patternCase) { 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); // Добавляем полученные statements в результат AddDesugaredCaseToResult(ifCheck, ifCheck); } private ident NewGeneralName() => new ident(GeneratedPatternNamePrefix + "GenVar" + generalVariableCounter++); private ident NewSuccessName() => new ident(GeneratedPatternNamePrefix + "Success" + successVariableCounter++); private ident NewEndIfName() => new ident(GeneratedPatternNamePrefix + "EndIf" + labelVariableCounter++); private bool IsGenerated(string name) => name.StartsWith(GeneratedPatternNamePrefix); private void AddDefaultCase(statement_list statements) { AddDesugaredCaseToResult(statements, _previousIf); } private void AddDesugaredCaseToResult(statement desugaredCase, if_node newIf) { // Если результат пустой, значит это первый case if (desugaredMatchWith == null) desugaredMatchWith = desugaredCase; else { _previousIf.else_body = desugaredCase; _previousIf.FillParentsInDirectChilds(); } // Запоминаем только что сгенерированный if _previousIf = newIf; } private DeconstructionDesugaringResult DesugarPattern(deconstructor_pattern pattern, expression matchingExpression) { Debug.Assert(!pattern.IsRecursive, "All recursive patterns should be desugared into simple patterns at this point"); var desugarResult = new DeconstructionDesugaringResult(); var castVariableName = NewGeneralName(); desugarResult.CastVariableDefinition = new var_statement(castVariableName, pattern.type); var successVariableName = NewSuccessName(); desugarResult.SuccessVariableDefinition = new var_statement(successVariableName, new ident("false")); // делегирование проверки паттерна функции IsTest desugarResult.TypeCastCheck = SubtreeCreator.CreateSystemFunctionCall(IsTestMethodName, matchingExpression, castVariableName); var parameters = pattern.parameters.Cast(); foreach (var deconstructedVariable in 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, parameters.Select(x => x.identifier).ToArray()); desugarResult.DeconstructCall = deconstructCall; return desugarResult; } private void DesugarIsExpression(is_pattern_expr isPatternExpr) { Debug.Assert(isPatternExpr.right is deconstructor_pattern); var patternLocation = GetLocation(isPatternExpr); var pattern = isPatternExpr.right as deconstructor_pattern; //AddDefinitionsInUpperStatementList(isPatternExpr, new[] { GetTypeCompatibilityCheck(isPatternExpr) }); if (pattern.IsRecursive) { var desugaredRecursiveIs = DesugarRecursiveDeconstructor(isPatternExpr.left, pattern); ReplaceUsingParent(isPatternExpr, desugaredRecursiveIs); desugaredRecursiveIs.visit(this); return; } switch (patternLocation) { case PatternLocation.IfCondition: DesugarIsExpressionInIfCondition(isPatternExpr); break; case PatternLocation.Assign: DesugarIsExpressionInAssignment(isPatternExpr); break; } } private expression DesugarRecursiveDeconstructor(expression expression, deconstructor_pattern pattern) { List 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) { //var parameterType = (parameter.pattern as deconstructor_pattern).type; var newName = NewGeneralName(); var varParameter = new var_deconstructor_parameter(newName, null); parameters[i] = varParameter; varParameter.Parent = parameters[i]; conjunction = bin_expr.LogicalAnd(conjunction, DesugarRecursiveDeconstructor(newName, parameter.pattern as deconstructor_pattern)); } } return conjunction; } private void DesugarIsExpressionInAssignment(is_pattern_expr isExpression) { var pattern = isExpression.right as deconstructor_pattern; var desugaringResult = DesugarPattern(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()); AddDefinitionsInUpperStatementList(isExpression, statementsToAdd); } private void DesugarIsExpressionInIfCondition(is_pattern_expr isExpression) { var pattern = isExpression.right as deconstructor_pattern; var desugaringResult = DesugarPattern(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()); var enclosingIf = GetAscendant(isExpression); // Если уже обрабатывался ранее (второй встретившийся в том же условии is), то не изменяем if if (processedIfNodes.Contains(enclosingIf)) AddDefinitionsInUpperStatementList(isExpression, statementsToAdd); // Иначе помещаем определения и if-then в отдельный блок, а else после этого блока else { // Сохраняем родителя, так как он поменяется при вызове ConvertIfNode var ifParent = enclosingIf.Parent; statement elseBody; var convertedIf = ConvertIfNode(enclosingIf, statementsToAdd, out elseBody); ifParent.ReplaceDescendantUnsafe(enclosingIf, convertedIf); convertedIf.Parent = ifParent; elseBody?.visit(this); } } private semantic_check_sugared_statement_node GetMatchedExpressionCheck(expression matchedExpression) => new semantic_check_sugared_statement_node(SemanticCheckType.MatchedExpression, new List() { matchedExpression }); private semantic_check_sugared_statement_node GetTypeCompatibilityCheck(is_pattern_expr expression) => new semantic_check_sugared_statement_node(SemanticCheckType.MatchedExpressionAndType, new List() { expression.left, (expression.right as deconstructor_pattern).type }); private statement_list ConvertIfNode(if_node ifNode, List statementsBeforeIf, out statement elseBody) { // if e then else // // переводим в // // begin // statementsBeforeIf // if e then begin ; goto end_if end; // end // // end_if: empty_statement // if e then // // переводим в // // begin // statementsBeforeIf // if e then // end // Добавляем, чтобы на конвертировать еще раз, если потребуется processedIfNodes.Add(ifNode); var statementsBeforeAndIf = new statement_list(); statementsBeforeAndIf.AddMany(statementsBeforeIf); statementsBeforeAndIf.Add(ifNode); if (ifNode.else_body == null) { elseBody = null; return statementsBeforeAndIf; } 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)); // Возвращаем else для обхода, т.к. он уже не входит в if elseBody = ifNode.else_body; // удаляем else из if ifNode.else_body = null; // Добавляем метку AddLabel(endIfLabel); return result; } } private void AddLabel(ident label) { var block = listNodes.OfType().Last(); if (block.defs == null) block.defs = new declarations(); block.defs.AddFirst(new label_definitions(label)); } private void AddDefinitionsInUpperStatementList(syntax_tree_node currentNode, IEnumerable statementsToAdd) { 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) { statements.InsertBefore( ascendants[i - 1] as statement, statementsToAdd); definitionsAdded = true; break; } } Debug.Assert(definitionsAdded, "Couldn't add definitions"); } private PatternLocation GetLocation(syntax_tree_node node) { var firstStatement = GetAscendant(node); switch (firstStatement) { case if_node _: return PatternLocation.IfCondition; case var_statement _: return PatternLocation.Assign; case assign _ : return PatternLocation.Assign; default: return PatternLocation.Unknown; } } private T GetAscendant(syntax_tree_node node) where T : syntax_tree_node { var current = node.Parent; while (current != null) { if (current is T res) return res; current = current.Parent; } return null; } } }