From 31e395dd6bd318108fcf08370d06c284a351cf89 Mon Sep 17 00:00:00 2001 From: "Sergey G. Grekhov" Date: Mon, 7 Nov 2022 15:43:30 +0200 Subject: [PATCH] #1401. [Patterns] logical-and pattern tests added (#1534) Authored by @sgrekhov. Logical-and pattern tests added. --- .../Patterns/logical_and_A01_t01.dart | 45 ++++++++++++++++ .../Patterns/logical_and_A01_t02.dart | 37 +++++++++++++ .../Patterns/logical_and_A02_t01.dart | 41 ++++++++++++++ .../Patterns/logical_and_A02_t02.dart | 42 +++++++++++++++ .../Patterns/logical_and_A03_t01.dart | 34 ++++++++++++ .../Patterns/logical_and_A04_t01.dart | 45 ++++++++++++++++ .../Patterns/logical_and_A04_t02.dart | 28 ++++++++++ .../Patterns/logical_and_A05_t01.dart | 54 +++++++++++++++++++ LanguageFeatures/Patterns/patterns_lib.dart | 50 ++++++++++++----- 9 files changed, 364 insertions(+), 12 deletions(-) create mode 100644 LanguageFeatures/Patterns/logical_and_A01_t01.dart create mode 100644 LanguageFeatures/Patterns/logical_and_A01_t02.dart create mode 100644 LanguageFeatures/Patterns/logical_and_A02_t01.dart create mode 100644 LanguageFeatures/Patterns/logical_and_A02_t02.dart create mode 100644 LanguageFeatures/Patterns/logical_and_A03_t01.dart create mode 100644 LanguageFeatures/Patterns/logical_and_A04_t01.dart create mode 100644 LanguageFeatures/Patterns/logical_and_A04_t02.dart create mode 100644 LanguageFeatures/Patterns/logical_and_A05_t01.dart diff --git a/LanguageFeatures/Patterns/logical_and_A01_t01.dart b/LanguageFeatures/Patterns/logical_and_A01_t01.dart new file mode 100644 index 0000000000..8e308d694e --- /dev/null +++ b/LanguageFeatures/Patterns/logical_and_A01_t01.dart @@ -0,0 +1,45 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// @assertion +/// logicalAndPattern ::= ( logicalAndPattern '&' )? relationalPattern +/// +/// A pair of patterns separated by & matches only if both subpatterns match. +/// Unlike logical-or patterns, the variables defined in each branch must not +/// overlap, since the logical-and pattern only matches if both branches do and +/// the variables in both branches will be bound. +/// +/// If the left branch does not match, the right branch is not evaluated. This +/// only matters because patterns may invoke user-defined methods with visible +/// side effects. +/// +/// @description Checks a logical-and pattern in a switch expression +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "../../Utils/expect.dart"; + +enum Color { + white, + red, + yellow, + blue, + black; +} + +bool isPrimary(Color color) { + return switch (color) { + case != Color.black & != Color.white => true; + default => false; + }; +} + +main() { + Expect.isFalse(isPrimary(Color.black)); + Expect.isFalse(isPrimary(Color.white)); + Expect.isTrue(isPrimary(Color.red)); + Expect.isTrue(isPrimary(Color.yellow)); + Expect.isTrue(isPrimary(Color.blue)); +} diff --git a/LanguageFeatures/Patterns/logical_and_A01_t02.dart b/LanguageFeatures/Patterns/logical_and_A01_t02.dart new file mode 100644 index 0000000000..15e3e3f814 --- /dev/null +++ b/LanguageFeatures/Patterns/logical_and_A01_t02.dart @@ -0,0 +1,37 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// @assertion +/// logicalAndPattern ::= ( logicalAndPattern '&' )? relationalPattern +/// +/// A pair of patterns separated by & matches only if both subpatterns match. +/// Unlike logical-or patterns, the variables defined in each branch must not +/// overlap, since the logical-and pattern only matches if both branches do and +/// the variables in both branches will be bound. +/// +/// If the left branch does not match, the right branch is not evaluated. This +/// only matters because patterns may invoke user-defined methods with visible +/// side effects. +/// +/// @description Checks a logical-and subpattern in a switch expression +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "../../Utils/expect.dart"; + +bool matches(List list) => switch (list) { + case [> 0 & <= 2, 3] => true; + default => false; +}; + +main() { + Expect.isFalse(matches([0, 3])); + Expect.isFalse(matches([2, 2])); + Expect.isFalse(matches([2, 3, 4])); + Expect.isFalse(matches([2, "3"])); + Expect.isFalse(matches(["1", 3])); + Expect.isTrue(matches([1, 3])); + Expect.isTrue(matches([2, 3])); +} diff --git a/LanguageFeatures/Patterns/logical_and_A02_t01.dart b/LanguageFeatures/Patterns/logical_and_A02_t01.dart new file mode 100644 index 0000000000..c7529b487c --- /dev/null +++ b/LanguageFeatures/Patterns/logical_and_A02_t01.dart @@ -0,0 +1,41 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// @assertion +/// logicalAndPattern ::= ( logicalAndPattern '&' )? relationalPattern +/// +/// A pair of patterns separated by & matches only if both subpatterns match. +/// Unlike logical-or patterns, the variables defined in each branch must not +/// overlap, since the logical-and pattern only matches if both branches do and +/// the variables in both branches will be bound. +/// +/// If the left branch does not match, the right branch is not evaluated. This +/// only matters because patterns may invoke user-defined methods with visible +/// side effects. +/// +/// @description Checks a logical-and pattern in a switch statement +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "patterns_lib.dart"; +import "../../Utils/expect.dart"; + +void test(int value, bool match) { + switch (value) { + case > 0 & <= 3: + Expect.isTrue(match); + break; + default: + Expect.isFalse(match); + } +} + +main() { + test(1, true); + test(2, true); + test(3, true); + test(0, false); + test(4, false); +} diff --git a/LanguageFeatures/Patterns/logical_and_A02_t02.dart b/LanguageFeatures/Patterns/logical_and_A02_t02.dart new file mode 100644 index 0000000000..34c383b1bc --- /dev/null +++ b/LanguageFeatures/Patterns/logical_and_A02_t02.dart @@ -0,0 +1,42 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// @assertion +/// logicalAndPattern ::= ( logicalAndPattern '&' )? relationalPattern +/// +/// A pair of patterns separated by & matches only if both subpatterns match. +/// Unlike logical-or patterns, the variables defined in each branch must not +/// overlap, since the logical-and pattern only matches if both branches do and +/// the variables in both branches will be bound. +/// +/// If the left branch does not match, the right branch is not evaluated. This +/// only matters because patterns may invoke user-defined methods with visible +/// side effects. +/// +/// @description Checks a logical-and subpattern in a switch statement +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "../../Utils/expect.dart"; + +bool matches(List list) { + bool result = false; + switch (list) { + case [> 0 & <= 2, 3]: + result = true; + default: + }; + return result; +} + +main() { + Expect.isFalse(matches([0, 3])); + Expect.isFalse(matches([2, 2])); + Expect.isFalse(matches([2, 3, 4])); + Expect.isFalse(matches([2, "3"])); + Expect.isFalse(matches(["1", 3])); + Expect.isTrue(matches([1, 3])); + Expect.isTrue(matches([2, 3])); +} diff --git a/LanguageFeatures/Patterns/logical_and_A03_t01.dart b/LanguageFeatures/Patterns/logical_and_A03_t01.dart new file mode 100644 index 0000000000..81a72ef141 --- /dev/null +++ b/LanguageFeatures/Patterns/logical_and_A03_t01.dart @@ -0,0 +1,34 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// @assertion +/// logicalAndPattern ::= ( logicalAndPattern '&' )? relationalPattern +/// +/// A pair of patterns separated by & matches only if both subpatterns match. +/// Unlike logical-or patterns, the variables defined in each branch must not +/// overlap, since the logical-and pattern only matches if both branches do and +/// the variables in both branches will be bound. +/// +/// If the left branch does not match, the right branch is not evaluated. This +/// only matters because patterns may invoke user-defined methods with visible +/// side effects. +/// +/// @description Checks a logical-and in a variable declaration pattern +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "../../Utils/expect.dart"; + +main() { + var r = (3.14, name: "pi"); + var ((a, name: b) & record) = r; + Expect.equals(3.14, a); + Expect.equals("pi", b); + Expect.equals(r, record); + + var (x & y) = 42; + Expect.equals(42, x); + Expect.equals(42, y); +} diff --git a/LanguageFeatures/Patterns/logical_and_A04_t01.dart b/LanguageFeatures/Patterns/logical_and_A04_t01.dart new file mode 100644 index 0000000000..6b8f48e650 --- /dev/null +++ b/LanguageFeatures/Patterns/logical_and_A04_t01.dart @@ -0,0 +1,45 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// @assertion +/// logicalAndPattern ::= ( logicalAndPattern '&' )? relationalPattern +/// +/// A pair of patterns separated by & matches only if both subpatterns match. +/// Unlike logical-or patterns, the variables defined in each branch must not +/// overlap, since the logical-and pattern only matches if both branches do and +/// the variables in both branches will be bound. +/// +/// If the left branch does not match, the right branch is not evaluated. This +/// only matters because patterns may invoke user-defined methods with visible +/// side effects. +/// +/// @description Checks that it is a compile-time error if two branches of +/// logical-and pattern define overlapping sets of variables +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "patterns_lib.dart"; + +main() { + Shape shape = Circle(1); + switch (shape) { + case Circle(area: var s) & Circle(area: var s): +// ^ +// [analyzer] unspecified +// [cfe] unspecified + break; + case Rectangle(x: var x, y: var width) & Rectangle(:var x, :var y): +// ^ +// [analyzer] unspecified +// [cfe] unspecified + break; + case Circle(area: var s1) & Circle(area: var s2) & Circle(area: var s1): +// ^^ +// [analyzer] unspecified +// [cfe] unspecified + default: + print("Other"); + } +} diff --git a/LanguageFeatures/Patterns/logical_and_A04_t02.dart b/LanguageFeatures/Patterns/logical_and_A04_t02.dart new file mode 100644 index 0000000000..fc47c4e8f0 --- /dev/null +++ b/LanguageFeatures/Patterns/logical_and_A04_t02.dart @@ -0,0 +1,28 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// @assertion +/// logicalAndPattern ::= ( logicalAndPattern '&' )? relationalPattern +/// +/// A pair of patterns separated by & matches only if both subpatterns match. +/// Unlike logical-or patterns, the variables defined in each branch must not +/// overlap, since the logical-and pattern only matches if both branches do and +/// the variables in both branches will be bound. +/// +/// If the left branch does not match, the right branch is not evaluated. This +/// only matters because patterns may invoke user-defined methods with visible +/// side effects. +/// +/// @description Checks that it is a compile-time error if two branches of +/// logical-and pattern define overlapping sets of variables +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +main() { + var ((a, name: b) & (a, name: n)) = (3.14, name: "pi"); +// ^ +// [analyzer] unspecified +// [cfe] unspecified +} diff --git a/LanguageFeatures/Patterns/logical_and_A05_t01.dart b/LanguageFeatures/Patterns/logical_and_A05_t01.dart new file mode 100644 index 0000000000..08ba23d1d3 --- /dev/null +++ b/LanguageFeatures/Patterns/logical_and_A05_t01.dart @@ -0,0 +1,54 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// @assertion +/// logicalAndPattern ::= ( logicalAndPattern '&' )? relationalPattern +/// +/// A pair of patterns separated by & matches only if both subpatterns match. +/// Unlike logical-or patterns, the variables defined in each branch must not +/// overlap, since the logical-and pattern only matches if both branches do and +/// the variables in both branches will be bound. +/// +/// If the left branch does not match, the right branch is not evaluated. This +/// only matters because patterns may invoke user-defined methods with visible +/// side effects. +/// +/// @description Checks that if the left branch does not match, the right branch +/// is not evaluated +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "patterns_lib.dart"; +import "../../Utils/expect.dart"; + +main() { + Shape shape1 = Circle(1); + switch (shape1) { + case Circle(area: 2) & Circle(size: 1): + Expect.fail("Pattern should not match"); + break; + default: + } + Expect.equals("Circle.area=2;", shape1.log); + + Shape shape2 = Square(1); + switch (shape2) { + case Square(area: 1) & Square(area: 2) & Square(size: 1): + Expect.fail("Pattern should not match"); + break; + default: + } + Expect.equals("Square.area=1;Square.area=2;", shape2.log); + + Shape shape3 = Square(1); + switch (shape3) { + case Shape(area: 1) & Shape(size: 1) & Square(area: 2) + & Square(size: 2): + Expect.fail("Pattern should not match"); + break; + default: + } + Expect.equals("Square.area=1;Square.size=1;Square.area=2;", shape3.log); +} diff --git a/LanguageFeatures/Patterns/patterns_lib.dart b/LanguageFeatures/Patterns/patterns_lib.dart index 862463c711..3e10c77d54 100644 --- a/LanguageFeatures/Patterns/patterns_lib.dart +++ b/LanguageFeatures/Patterns/patterns_lib.dart @@ -9,17 +9,17 @@ library patterns_lib; -class Area { +class Loggable { final double value; final void Function(String s)? _logger; - const Area(this.value) : _logger = null; - const Area.withLogger(this.value, this._logger); + const Loggable(this.value) : _logger = null; + const Loggable.withLogger(this.value, this._logger); @override bool operator ==(Object other) { final _log = _logger; final tolerance = 0.001; - if (other is Area) { + if (other is Loggable) { if (_log != null) { _log("=$other;"); } @@ -52,9 +52,14 @@ class Shape { _log = _log.isEmpty ? toAppend : "$_log$toAppend"; } - Area get area { + Loggable get area { logger("Shape.area"); - return Area.withLogger(0, logger); + return Loggable.withLogger(0, logger); + } + + Loggable get size { + logger("Shape.size"); + return Loggable.withLogger(0, logger); } String get log => _log; @@ -65,9 +70,16 @@ class Square extends Shape { Square(this.length); - Area get area { + @override + Loggable get area { logger("Square.area"); - return Area.withLogger(length * length, logger); + return Loggable.withLogger(length * length, logger); + } + + @override + Loggable get size { + logger("Square.size"); + return Loggable.withLogger(length, logger); } } @@ -76,9 +88,16 @@ class Circle extends Shape { Circle(this.radius); - Area get area { + @override + Loggable get area { logger("Circle.area"); - return Area.withLogger(3.14 * radius * radius, logger); + return Loggable.withLogger(3.14 * radius * radius, logger); + } + + @override + Loggable get size { + logger("Circle.size"); + return Loggable.withLogger(radius, logger); } } @@ -87,8 +106,15 @@ class Rectangle extends Shape { Rectangle(this.x, this.y); - Area get area { + @override + Loggable get area { logger("Rectangle.area"); - return Area.withLogger(x * y, logger); + return Loggable.withLogger(x * y, logger); + } + + @override + Loggable get size { + logger("Rectangle.size"); + return Loggable.withLogger(x, logger); } }