diff --git a/Parsers/PascalABCParserNewSaushkin/ABCPascal.cs b/Parsers/PascalABCParserNewSaushkin/ABCPascal.cs index 6a1d7f1db..1f1d8a219 100644 --- a/Parsers/PascalABCParserNewSaushkin/ABCPascal.cs +++ b/Parsers/PascalABCParserNewSaushkin/ABCPascal.cs @@ -2,7 +2,7 @@ // This CSharp output file generated by Gardens Point LEX // Version: 1.1.3.301 // Machine: ALEXANDER-PC -// DateTime: 11.05.2018 13:59:31 +// DateTime: 11.05.2018 18:06:46 // UserName: Alexander // GPLEX input file // GPLEX frame file diff --git a/Parsers/PascalABCParserNewSaushkin/ABCPascal.y b/Parsers/PascalABCParserNewSaushkin/ABCPascal.y index 4fdf467f1..beb623831 100644 --- a/Parsers/PascalABCParserNewSaushkin/ABCPascal.y +++ b/Parsers/PascalABCParserNewSaushkin/ABCPascal.y @@ -2537,9 +2537,9 @@ if_stmt ; match_with - : tkMatch expr_l1 tkWith pattern_cases tkEnd + : tkMatch expr_l1 tkWith pattern_cases else_case tkEnd { - $$ = new match_with($2, $4 as pattern_cases); + $$ = new match_with($2, $4 as pattern_cases, $5 as statement, @$); } ; @@ -2559,11 +2559,11 @@ pattern_case { $$ = new empty_statement(); } | pattern tkWhen expr_l1 tkColon unlabelled_stmt { - $$ = new pattern_case($1 as pattern_node, $5 as statement, $3); + $$ = new pattern_case($1 as pattern_node, $5 as statement, $3, @$); } | pattern tkColon unlabelled_stmt { - $$ = new pattern_case($1 as pattern_node, $3 as statement, null); + $$ = new pattern_case($1 as pattern_node, $3 as statement, null, @$); } ; diff --git a/Parsers/PascalABCParserNewSaushkin/ABCPascalYacc.cs b/Parsers/PascalABCParserNewSaushkin/ABCPascalYacc.cs index c76a51f53..012f45430 100644 --- a/Parsers/PascalABCParserNewSaushkin/ABCPascalYacc.cs +++ b/Parsers/PascalABCParserNewSaushkin/ABCPascalYacc.cs @@ -2,7 +2,7 @@ // GPPG version 1.3.6 // Machine: ALEXANDER-PC -// DateTime: 11.05.2018 13:59:32 +// DateTime: 11.05.2018 18:06:47 // UserName: Alexander // Input file @@ -75,7 +75,7 @@ public partial class GPPGParser: ShiftReduceParser aliasses; #pragma warning restore 649 private static Rule[] rules = new Rule[868]; - private static State[] states = new State[1413]; + private static State[] states = new State[1414]; private static string[] nonTerms = new string[] { "parse_goal", "unit_key_word", "assignment", "optional_array_initializer", "attribute_declarations", "ot_visibility_specifier", "one_attribute", "attribute_variable", @@ -157,13 +157,13 @@ public partial class GPPGParser: ShiftReduceParser tkMatch, expr_l1, tkWith, pattern_cases, tkEnd + case 506: // match_with -> tkMatch, expr_l1, tkWith, pattern_cases, else_case, tkEnd { - CurrentSemanticValue.stn = new match_with(ValueStack[ValueStack.Depth-4].ex, ValueStack[ValueStack.Depth-2].stn as pattern_cases); + CurrentSemanticValue.stn = new match_with(ValueStack[ValueStack.Depth-5].ex, ValueStack[ValueStack.Depth-3].stn as pattern_cases, ValueStack[ValueStack.Depth-2].stn as statement, CurrentLocationSpan); } break; case 507: // pattern_cases -> pattern_case @@ -4726,12 +4727,12 @@ public partial class GPPGParser: ShiftReduceParser pattern, tkWhen, expr_l1, tkColon, unlabelled_stmt { - CurrentSemanticValue.stn = new pattern_case(ValueStack[ValueStack.Depth-5].stn as pattern_node, ValueStack[ValueStack.Depth-1].stn as statement, ValueStack[ValueStack.Depth-3].ex); + CurrentSemanticValue.stn = new pattern_case(ValueStack[ValueStack.Depth-5].stn as pattern_node, ValueStack[ValueStack.Depth-1].stn as statement, ValueStack[ValueStack.Depth-3].ex, CurrentLocationSpan); } break; case 511: // pattern_case -> pattern, tkColon, unlabelled_stmt { - CurrentSemanticValue.stn = new pattern_case(ValueStack[ValueStack.Depth-3].stn as pattern_node, ValueStack[ValueStack.Depth-1].stn as statement, null); + CurrentSemanticValue.stn = new pattern_case(ValueStack[ValueStack.Depth-3].stn as pattern_node, ValueStack[ValueStack.Depth-1].stn as statement, null, CurrentLocationSpan); } break; case 512: // case_stmt -> tkCase, expr_l1, tkOf, case_list, else_case, tkEnd diff --git a/SyntaxTree/tree/HierarchyVisitor.cs b/SyntaxTree/tree/HierarchyVisitor.cs index 0a6b60459..a005c7ac7 100644 --- a/SyntaxTree/tree/HierarchyVisitor.cs +++ b/SyntaxTree/tree/HierarchyVisitor.cs @@ -3787,6 +3787,7 @@ namespace PascalABCCompiler.SyntaxTree pre_do_visit(_match_with); visit(match_with.expr); visit(match_with.case_list); + visit(match_with.defaultAction); post_do_visit(_match_with); } diff --git a/SyntaxTree/tree/SyntaxTreeStreamReader.cs b/SyntaxTree/tree/SyntaxTreeStreamReader.cs index dd2adcd6d..a4d904872 100644 --- a/SyntaxTree/tree/SyntaxTreeStreamReader.cs +++ b/SyntaxTree/tree/SyntaxTreeStreamReader.cs @@ -3986,6 +3986,7 @@ namespace PascalABCCompiler.SyntaxTree read_statement(_match_with); _match_with.expr = _read_node() as expression; _match_with.case_list = _read_node() as pattern_cases; + _match_with.defaultAction = _read_node() as statement; } diff --git a/SyntaxTree/tree/SyntaxTreeStreamWriter.cs b/SyntaxTree/tree/SyntaxTreeStreamWriter.cs index 6a128ddc0..6528094d9 100644 --- a/SyntaxTree/tree/SyntaxTreeStreamWriter.cs +++ b/SyntaxTree/tree/SyntaxTreeStreamWriter.cs @@ -6239,6 +6239,15 @@ namespace PascalABCCompiler.SyntaxTree bw.Write((byte)1); _match_with.case_list.visit(this); } + if (_match_with.defaultAction == null) + { + bw.Write((byte)0); + } + else + { + bw.Write((byte)1); + _match_with.defaultAction.visit(this); + } } diff --git a/SyntaxTree/tree/Tree.cs b/SyntaxTree/tree/Tree.cs index 18d556ead..cb290fd0b 100644 --- a/SyntaxTree/tree/Tree.cs +++ b/SyntaxTree/tree/Tree.cs @@ -47878,25 +47878,28 @@ namespace PascalABCCompiler.SyntaxTree /// ///Конструктор с параметрами. /// - public match_with(expression _expr,pattern_cases _case_list) + public match_with(expression _expr,pattern_cases _case_list,statement _defaultAction) { this._expr=_expr; this._case_list=_case_list; + this._defaultAction=_defaultAction; FillParentsInDirectChilds(); } /// ///Конструктор с параметрами. /// - public match_with(expression _expr,pattern_cases _case_list,SourceContext sc) + public match_with(expression _expr,pattern_cases _case_list,statement _defaultAction,SourceContext sc) { this._expr=_expr; this._case_list=_case_list; + this._defaultAction=_defaultAction; source_context = sc; FillParentsInDirectChilds(); } protected expression _expr; protected pattern_cases _case_list; + protected statement _defaultAction; /// /// @@ -47928,6 +47931,21 @@ namespace PascalABCCompiler.SyntaxTree } } + /// + /// + /// + public statement defaultAction + { + get + { + return _defaultAction; + } + set + { + _defaultAction=value; + } + } + /// Создает копию узла public override syntax_tree_node Clone() @@ -47951,6 +47969,11 @@ namespace PascalABCCompiler.SyntaxTree copy.case_list = (pattern_cases)case_list.Clone(); copy.case_list.Parent = copy; } + if (defaultAction != null) + { + copy.defaultAction = (statement)defaultAction.Clone(); + copy.defaultAction.Parent = copy; + } return copy; } @@ -47969,6 +47992,8 @@ namespace PascalABCCompiler.SyntaxTree expr.Parent = this; if (case_list != null) case_list.Parent = this; + if (defaultAction != null) + defaultAction.Parent = this; } /// Заполняет поля Parent во всем поддереве @@ -47978,6 +48003,7 @@ namespace PascalABCCompiler.SyntaxTree attributes?.FillParentsInAllChilds(); expr?.FillParentsInAllChilds(); case_list?.FillParentsInAllChilds(); + defaultAction?.FillParentsInAllChilds(); } /// @@ -47987,7 +48013,7 @@ namespace PascalABCCompiler.SyntaxTree { get { - return 2; + return 3; } } /// @@ -47997,7 +48023,7 @@ namespace PascalABCCompiler.SyntaxTree { get { - return 2; + return 3; } } /// @@ -48015,6 +48041,8 @@ namespace PascalABCCompiler.SyntaxTree return expr; case 1: return case_list; + case 2: + return defaultAction; } return null; } @@ -48030,6 +48058,9 @@ namespace PascalABCCompiler.SyntaxTree case 1: case_list = (pattern_cases)value; break; + case 2: + defaultAction = (statement)value; + break; } } } diff --git a/SyntaxTree/tree/TreeHelper.cs b/SyntaxTree/tree/TreeHelper.cs index 40fe79dd7..f50d04adf 100644 --- a/SyntaxTree/tree/TreeHelper.cs +++ b/SyntaxTree/tree/TreeHelper.cs @@ -382,6 +382,11 @@ namespace PascalABCCompiler.SyntaxTree return new bin_expr(left, right, Operators.Less); } + public static bin_expr LogicalAnd(expression left, expression right) + { + return new bin_expr(left, right, Operators.LogicalAND); + } + public override string ToString() { return string.Format("{0} {2} {1}", left, right, OperatorServices.ToString(operation_type, LanguageId.PascalABCNET)); diff --git a/SyntaxTree/tree/tree.xml b/SyntaxTree/tree/tree.xml index 3738e34c8..7f97aff16 100644 --- a/SyntaxTree/tree/tree.xml +++ b/SyntaxTree/tree/tree.xml @@ -2901,6 +2901,7 @@ + @@ -3680,6 +3681,7 @@ + @@ -3841,6 +3843,7 @@ + diff --git a/SyntaxVisitors/SugarVisitors/MatchWithVisitor.cs b/SyntaxVisitors/SugarVisitors/MatchWithVisitor.cs index e2e77c91e..4b03512b8 100644 --- a/SyntaxVisitors/SugarVisitors/MatchWithVisitor.cs +++ b/SyntaxVisitors/SugarVisitors/MatchWithVisitor.cs @@ -47,7 +47,7 @@ namespace SyntaxVisitors.SugarVisitors private int _variableCounter = 0; private if_node _previousIf; - private statement_list desugaredMatchWith; + private statement desugaredMatchWith; public static MatchWithVisitor New => new MatchWithVisitor(); @@ -67,8 +67,11 @@ namespace SyntaxVisitors.SugarVisitors DesugarDeconstructorPatternCase(matchWith.expr, patternCase); } + if (matchWith.defaultAction != null) + AddDefaultCase(matchWith.defaultAction as statement_list); + if (desugaredMatchWith == null) - return; + desugaredMatchWith = new empty_statement(); // Замена выражения match with на новое несахарное поддерево и его обход ReplaceUsingParent(matchWith, desugaredMatchWith); @@ -116,18 +119,23 @@ namespace SyntaxVisitors.SugarVisitors var desugaredPattern = DesugarPattern(pattern, matchingExpression); result.Add(desugaredPattern.CastVariableDefinition); - if (patternCase.condition != null) - // Если есть секция when - добавляем ее в условие - { - // TODO: generate check for 'when' - } + // Создание тела if из объявлений переменных, вызова Deconstruct и соответствующего тела case var ifBody = new statement_list(); ifBody.Add(new desugared_deconstruction(desugaredPattern.DeconstructionVariables, desugaredPattern.CastVariable, patternCase.pattern.source_context)); ifBody.Add(desugaredPattern.DeconstructCall); - ifBody.Add(patternCase.case_action); - var ifCheck = SubtreeCreator.CreateIf(desugaredPattern.TypeCastCheck, ifBody); + + if (patternCase.condition != null) + { + // Выполняем тело, если выполняется условие when + ifBody.Add(SubtreeCreator.CreateIf(patternCase.condition, patternCase.case_action)); + } + else + ifBody.Add(patternCase.case_action); + + var ifCondition = desugaredPattern.TypeCastCheck; + var ifCheck = SubtreeCreator.CreateIf(ifCondition, ifBody); result.Add(ifCheck); @@ -140,13 +148,21 @@ namespace SyntaxVisitors.SugarVisitors return new ident("<>patternGenVar" + _variableCounter++); } + private void AddDefaultCase(statement_list statements) + { + AddDesugaredCaseToResult(statements, _previousIf); + } + private void AddDesugaredCaseToResult(statement_list statements, if_node newIf) { // Если результат пустой, значит это первый case if (desugaredMatchWith == null) desugaredMatchWith = statements; else + { _previousIf.else_body = statements; + _previousIf.FillParentsInDirectChilds(); + } // Запоминаем только что сгенерированный if _previousIf = newIf; diff --git a/TestSuite/patterns-deconstruct-default.pas b/TestSuite/patterns-deconstruct-default.pas new file mode 100644 index 000000000..e6342dc3c --- /dev/null +++ b/TestSuite/patterns-deconstruct-default.pas @@ -0,0 +1,15 @@ +begin + match 'asd' with + string(s): + match s with + integer(i): Assert(false); + string(s1) when s.Length > 1: ; + else Assert(false); + end; + end; + + match 'asd' with + integer(i): Assert(false); + string(s1) when s1.Length > 3: Assert(false); + end; +end. \ No newline at end of file diff --git a/TestSuite/patterns-deconstruct-generic-extension.pas b/TestSuite/patterns-deconstruct-generic-extension.pas new file mode 100644 index 000000000..e97429a53 --- /dev/null +++ b/TestSuite/patterns-deconstruct-generic-extension.pas @@ -0,0 +1,34 @@ + +procedure Deconstruct(self: List; var f: T); extensionmethod; +begin + if self.Count > 0 then f := self[0]; +end; + +procedure Deconstruct(self: Dictionary>; var f: T); extensionmethod; +begin + var fst := self.First; + var lst := fst.Value; + if not (lst = nil) and (lst.Count > 0) then + f := lst[0]; +end; + +procedure Deconstruct(self: Dictionary; var kk: Dictionary, V>; var vv: V); extensionmethod; +begin + kk := Dict((self.Keys.ToList, self.Values.First)); +end; + +begin + var pair := ('asd', Arr(1,2,3).ToList); + var lst := Arr(4,5,6).ToList as object; + var d := Dict(Arr(pair)) as object; + var d2 := Dict((7, 8.0)) as object; + var collection := Arr(lst, d, d2); + foreach var x in collection do + match x with + Dictionary(s; k): Assert((s is Dictionary, real>) and (k is real)); + Dictionary>(first): Assert(first['asd'][0] = 1); + List(l: real): Assert(false); + List(first: integer): Assert(first = 4); + else Assert(false) + end; +end. \ No newline at end of file diff --git a/TestSuite/patterns-lambda-test.pas b/TestSuite/patterns-lambda-test.pas new file mode 100644 index 000000000..b8014ac3f --- /dev/null +++ b/TestSuite/patterns-lambda-test.pas @@ -0,0 +1,38 @@ +uses System.Linq; + +begin + match 'a b c'.Any(x -> x = 'a') with + string(s): Assert(false); + boolean(b): Assert(b); + integer(i): Assert(false); + else Assert(false) + end; + + var count := 0; + match Arr('aa', 'bb').Select(x -> x = 'aa' ? x : nil).ToList with + List(l): + foreach var x in l do + if not (x = nil) then + loop 5 do count += 1; + end; + Assert(count = 5); + + match 'a b c'.Any(x -> x = 'a') with + string(s): Assert(false); + boolean(b): + begin + var r := Enumerable + .Repeat(b, 3) + .Select( + x -> + begin + match x with + boolean(b): Result := not b; + end; + end); + Assert(Enumerable.Repeat(false, 3).SequenceEqual(r)); + end; + integer(i): Assert(false); + else Assert(false); + end; +end. \ No newline at end of file diff --git a/TestSuite/patterns-nil-test.pas b/TestSuite/patterns-nil-test.pas new file mode 100644 index 000000000..cfe9982d2 --- /dev/null +++ b/TestSuite/patterns-nil-test.pas @@ -0,0 +1,6 @@ +begin + match nil with + List(s): Assert(false); + integer(i): Assert(false); + end; +end. \ No newline at end of file diff --git a/TestSuite/patterns-overloading-and-ambiguity.pas b/TestSuite/patterns-overloading-and-ambiguity.pas new file mode 100644 index 000000000..279ea6af3 --- /dev/null +++ b/TestSuite/patterns-overloading-and-ambiguity.pas @@ -0,0 +1,28 @@ +type + My = class + public + _a: T; + + private + procedure Deconstruct(var a: T); + begin + Println('Instance'); + a := _a; + end; + end; + +procedure Deconstruct(self: My; var a: T); +begin + Println('Ext'); + a := self._a; +end; + +begin + var l := new My>; + l._a := Arr(1, 2, 3).ToList; + match l with + My(a): Assert(false); + My(a): Assert(false); + My>(a): Assert(a[0] = 1); + end; +end. \ No newline at end of file diff --git a/TreeConverter/LambdaExpressions/LambdaResultTypeInferrer.cs b/TreeConverter/LambdaExpressions/LambdaResultTypeInferrer.cs index 73576e4d1..24a697a11 100644 --- a/TreeConverter/LambdaExpressions/LambdaResultTypeInferrer.cs +++ b/TreeConverter/LambdaExpressions/LambdaResultTypeInferrer.cs @@ -69,6 +69,14 @@ namespace TreeConverter.LambdaExpressions syntaxTreeVisitor.visit(varStmt); } + // Patterns + public override void visit(desugared_deconstruction _desugared_deconstruction) + { + // позволяем вывести типы объявленных переменных + syntaxTreeVisitor.visit(_desugared_deconstruction); + } + // !Patterns + public override void visit(assign assignment) { var to = assignment.to as ident; diff --git a/TreeConverter/TreeConversion/SyntaxTreeVisitorNodes/Patterns.cs b/TreeConverter/TreeConversion/SyntaxTreeVisitorNodes/Patterns.cs index 80e976e46..4f165a9df 100644 --- a/TreeConverter/TreeConversion/SyntaxTreeVisitorNodes/Patterns.cs +++ b/TreeConverter/TreeConversion/SyntaxTreeVisitorNodes/Patterns.cs @@ -11,16 +11,6 @@ namespace PascalABCCompiler.TreeConverter // Patterns public partial class syntax_tree_visitor { - private void CheckWhenExpression(SyntaxTree.expression when) - { - if (when == null) - return; - - var convertedWhen = convert_strong(when); - if (!convertion_data_and_alghoritms.can_convert_type(convertedWhen, SystemLibrary.SystemLibrary.bool_type)) - AddError(get_location(when), "WHEN_EXPRESSION_SHOULD_HAVE_BOOL_TYPE"); - } - public override void visit(desugared_deconstruction deconstruction) { var invokationTarget = convert_strong(deconstruction.deconstruction_target as expression); @@ -94,7 +84,7 @@ namespace PascalABCCompiler.TreeConverter { parameterTypes = new type_node[givenParameterTypes.Length]; var selfParameter = candidate.is_extension_method ? candidate.parameters.First(IsSelfParameter) : null; - Debug.Assert(!candidate.is_extension_method || selfParameter != null, "Couldn't find self in extension method"); + Debug.Assert(!candidate.is_extension_method || selfParameter != null, "Couldn't find self parameter in extension method"); var candidateParameterTypes = candidate.is_extension_method ? candidate.parameters.Where(x => !IsSelfParameter(x)).ToArray() : @@ -111,10 +101,11 @@ namespace PascalABCCompiler.TreeConverter var nils = new List(); var deduceSucceded = generic_convertions.DeduceInstanceTypes(selfParameter.type, patternInstance.type, deducedGenerics, nils); if (!deduceSucceded || deducedGenerics.Contains(null)) - { - AddError(deconstructionLocation, "COULDNT_DEDUCE_DECONSTRUCT_GENERIC_TYPE"); + // Проверка на то, что в Deconstruct все дженерики выводятся по self делается в другом месте + // TODO: сделать проверку + // TODO: запретить дженерик методы в классах. Можно использовать только дженерик-типы самого класса в качестве параметров + //AddError(deconstructionLocation, "COULDNT_DEDUCE_DECONSTRUCT_GENERIC_TYPE"); return false; - } } for (int i = 0; i < givenParameterTypes.Length; i++) @@ -122,7 +113,7 @@ namespace PascalABCCompiler.TreeConverter var givenParameter = givenParameterTypes[i]; var candidateParameter = candidateParameterTypes[i].type; if (genericDeduceNeeded && (candidateParameter.is_generic_parameter || candidateParameter.is_generic_type_instance)) - candidateParameter = InstantiateParameter(candidateParameter, deducedGenerics);//candidateParameter.get_instance(deducedGenerics.ToList()); + candidateParameter = InstantiateParameter(candidateParameter, deducedGenerics); if (givenParameter != null && !AreTheSameTypes(candidateParameter, givenParameter)) return false; @@ -183,11 +174,6 @@ namespace PascalABCCompiler.TreeConverter var genericNode = (genericType as generic_instance_type_node); if (genericNode != null) { - //var genericParameters = genericNode.instance_params; - - //for (int i = 0; i < genericParameters.Count; i++) - // genericParameters[i] = InstantiateParameter(genericParameters[i], instances); - var parameters = genericNode.instance_params.Select(x => InstantiateParameter(x, instances)).ToList(); genericNode.instance_params = parameters; }