diff --git a/LanguageFeatures/Patterns/logical_or_A01_t01.dart b/LanguageFeatures/Patterns/logical_or_A01_t01.dart new file mode 100644 index 0000000000..173569670b --- /dev/null +++ b/LanguageFeatures/Patterns/logical_or_A01_t01.dart @@ -0,0 +1,48 @@ +// 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 logicalOrPattern ::= ( logicalOrPattern '|' )? logicalAndPattern +/// +/// A pair of patterns separated by | matches if either of the branches match +/// +/// A logical-or pattern may match even if one of its branches does not. That +/// means that any variables in the non-matching branch would not be +/// initialized. To avoid problems stemming from that, the following +/// restrictions apply: +/// +/// The two branches must define the same set of variables. +/// +/// If the left branch matches, the right branch is not evaluated. This +/// determines which value the variable gets if both branches would have +/// matched. In that case, it will always be the value from the left branch. +/// +/// @description Checks logical-or 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.red | Color.yellow | Color.blue => 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_or_A01_t02.dart b/LanguageFeatures/Patterns/logical_or_A01_t02.dart new file mode 100644 index 0000000000..124a0aba1b --- /dev/null +++ b/LanguageFeatures/Patterns/logical_or_A01_t02.dart @@ -0,0 +1,40 @@ +// 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 logicalOrPattern ::= ( logicalOrPattern '|' )? logicalAndPattern +/// +/// A pair of patterns separated by | matches if either of the branches match +/// +/// A logical-or pattern may match even if one of its branches does not. That +/// means that any variables in the non-matching branch would not be +/// initialized. To avoid problems stemming from that, the following +/// restrictions apply: +/// +/// The two branches must define the same set of variables. +/// +/// If the left branch matches, the right branch is not evaluated. This +/// determines which value the variable gets if both branches would have +/// matched. In that case, it will always be the value from the left branch. +/// +/// @description Checks logical-or subpattern in a switch expression +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "../../Utils/expect.dart"; + +bool matches(List list) => switch (list) { + case [1 | 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_or_A02_t01.dart b/LanguageFeatures/Patterns/logical_or_A02_t01.dart new file mode 100644 index 0000000000..ef99298219 --- /dev/null +++ b/LanguageFeatures/Patterns/logical_or_A02_t01.dart @@ -0,0 +1,46 @@ +// 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 logicalOrPattern ::= ( logicalOrPattern '|' )? logicalAndPattern +/// +/// A pair of patterns separated by | matches if either of the branches match +/// +/// A logical-or pattern may match even if one of its branches does not. That +/// means that any variables in the non-matching branch would not be +/// initialized. To avoid problems stemming from that, the following +/// restrictions apply: +/// +/// The two branches must define the same set of variables. +/// +/// If the left branch matches, the right branch is not evaluated. This +/// determines which value the variable gets if both branches would have +/// matched. In that case, it will always be the value from the left branch. +/// +/// @description Checks logical-or pattern in a switch statement +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "patterns_lib.dart"; +import "../../Utils/expect.dart"; + +void test(Shape shape, double expectedArea, Type expectedType, bool match) { + switch (shape) { + case Square(area: var s) | Circle(area: var s): + Expect.equals(s, expectedArea); + Expect.equals(expectedType, shape.runtimeType); + Expect.isTrue(match); + break; + default: + Expect.equals(s, expectedArea); + Expect.equals(expectedType, shape.runtimeType); + Expect.isFalse(match); + } +} + +main() { + test(Circle(1), 3.14, Circle, true); + test(Square(2), 4, Square, true); + test(Rectangle(2, 1), 2, Rectangle, false); +} diff --git a/LanguageFeatures/Patterns/logical_or_A02_t02.dart b/LanguageFeatures/Patterns/logical_or_A02_t02.dart new file mode 100644 index 0000000000..56edc1f4bb --- /dev/null +++ b/LanguageFeatures/Patterns/logical_or_A02_t02.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 logicalOrPattern ::= ( logicalOrPattern '|' )? logicalAndPattern +/// +/// A pair of patterns separated by | matches if either of the branches match +/// +/// A logical-or pattern may match even if one of its branches does not. That +/// means that any variables in the non-matching branch would not be +/// initialized. To avoid problems stemming from that, the following +/// restrictions apply: +/// +/// The two branches must define the same set of variables. +/// +/// If the left branch matches, the right branch is not evaluated. This +/// determines which value the variable gets if both branches would have +/// matched. In that case, it will always be the value from the left branch. +/// +/// @description Checks logical-or 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 [1 | 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_or_A03_t01.dart b/LanguageFeatures/Patterns/logical_or_A03_t01.dart new file mode 100644 index 0000000000..1df39dd290 --- /dev/null +++ b/LanguageFeatures/Patterns/logical_or_A03_t01.dart @@ -0,0 +1,46 @@ +// 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 logicalOrPattern ::= ( logicalOrPattern '|' )? logicalAndPattern +/// +/// A pair of patterns separated by | matches if either of the branches match +/// +/// A logical-or pattern may match even if one of its branches does not. That +/// means that any variables in the non-matching branch would not be +/// initialized. To avoid problems stemming from that, the following +/// restrictions apply: +/// +/// The two branches must define the same set of variables. +/// +/// If the left branch matches, the right branch is not evaluated. This +/// determines which value the variable gets if both branches would have +/// matched. In that case, it will always be the value from the left branch. +/// +/// @description Checks that it is a compile-time error if two branches of +/// logical-or pattern define different sets of variables +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "patterns_lib.dart"; + +main() { + Shape shape = Circle(1); + switch (shape) { + case Square(area: var s1) | Circle(area: var s2): +// ^^ +// [analyzer] unspecified +// [cfe] unspecified + print("Square or Circle"); + break; + case Rectangle(x: var x, y: var width) | Rectangle(:var x, :var y): +// ^ +// [analyzer] unspecified +// [cfe] unspecified + print("Rectangle"); + break; + default: + print("Other"); + } +} diff --git a/LanguageFeatures/Patterns/logical_or_A04_t01.dart b/LanguageFeatures/Patterns/logical_or_A04_t01.dart new file mode 100644 index 0000000000..38e5a42901 --- /dev/null +++ b/LanguageFeatures/Patterns/logical_or_A04_t01.dart @@ -0,0 +1,56 @@ +// 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 logicalOrPattern ::= ( logicalOrPattern '|' )? logicalAndPattern +/// +/// A pair of patterns separated by | matches if either of the branches match +/// +/// A logical-or pattern may match even if one of its branches does not. That +/// means that any variables in the non-matching branch would not be +/// initialized. To avoid problems stemming from that, the following +/// restrictions apply: +/// +/// The two branches must define the same set of variables. +/// +/// If the left branch matches, the right branch is not evaluated. This +/// determines which value the variable gets if both branches would have +/// matched. In that case, it will always be the value from the left branch. +/// +/// @description Checks that if the left branch matches, 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 = Square(1); + switch (shape1) { + case Square(area: var s) | Shape(area: var s): + Expect.equals("Square.area", shape1.log); + break; + default: + print("Other"); + } + + Shape shape2 = Square(2); + switch (shape2) { + case Square(area: 2) | Square(area: 4) | Square(area: 4): + Expect.equals("Circle.area=2;Circle.area=4;", shape2.log); + break; + default: + print("Other"); + } + + Shape shape3 = Shape(); + switch (shape3) { + case Circle(area: 0) | Square(area: 1) | Shape(area: 0): + Expect.equals("Shape.area=0;", shape3.log); + break; + default: + print("Other"); + } +} diff --git a/LanguageFeatures/Patterns/logical_or_A04_t02.dart b/LanguageFeatures/Patterns/logical_or_A04_t02.dart new file mode 100644 index 0000000000..e0d1067db5 --- /dev/null +++ b/LanguageFeatures/Patterns/logical_or_A04_t02.dart @@ -0,0 +1,72 @@ +// 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 logicalOrPattern ::= ( logicalOrPattern '|' )? logicalAndPattern +/// +/// A pair of patterns separated by | matches if either of the branches match +/// +/// A logical-or pattern may match even if one of its branches does not. That +/// means that any variables in the non-matching branch would not be +/// initialized. To avoid problems stemming from that, the following +/// restrictions apply: +/// +/// The two branches must define the same set of variables. +/// +/// If the left branch matches, the right branch is not evaluated. This +/// determines which value the variable gets if both branches would have +/// matched. In that case, it will always be the value from the left branch. +/// +/// @description Checks that if the left branch doesn't match, then the right +/// branch is evaluated +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +import "patterns_lib.dart"; +import "../../Utils/expect.dart"; + +main() { + Shape shape1 = Square(1); + switch (shape1) { + case Square(area: 2) | Square(area: 1): + Expect.equals("Square.area=2;Square.area=1;", shape1.log); + break; + default: + print("Other"); + } + + Shape shape2 = Shape(); + switch (shape2) { + case Square(area: 2) | Rectangle(area: 1) | Shape(area: 0): + Expect.equals("Shape.area=0;", shape2.log); + break; + default: + print("Other"); + } + + Shape shape3 = Circle(1); + switch (shape2) { + case Circle(area: 2) | Circle(area: 1) | Circle(area: 0) + | Circle(area: 3.14): + Expect.equals( + "Circle.area=2;Circle.area=1;Circle.area=0;Circle.area=3.14;", + shape3.log); + break; + default: + print("Other"); + } + + Shape shape4 = Rectangle(1, 2); + bool other = false; + switch (shape4) { + case Rectangle(area: 3) | Rectangle(area: 1) | Rectangle(area: 42): + Expect.fail("No branches should match"); + break; + default: + other = true; + Expect.equals("Rectangle.area=3;Rectangle.area=1;Rectangle.area=42;", + shape4.log); + } + Expect.isTrue(other); +} diff --git a/LanguageFeatures/Patterns/patterns_lib.dart b/LanguageFeatures/Patterns/patterns_lib.dart new file mode 100644 index 0000000000..862463c711 --- /dev/null +++ b/LanguageFeatures/Patterns/patterns_lib.dart @@ -0,0 +1,94 @@ +// 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. + +/// @description Defines data commonly used by Patterns tests +/// @author sgrekhov22@gmail.com + +// SharedOptions=--enable-experiment=patterns + +library patterns_lib; + +class Area { + final double value; + final void Function(String s)? _logger; + const Area(this.value) : _logger = null; + const Area.withLogger(this.value, this._logger); + + @override + bool operator ==(Object other) { + final _log = _logger; + final tolerance = 0.001; + if (other is Area) { + if (_log != null) { + _log("=$other;"); + } + return (this.value - other.value).abs() <= tolerance; + } + if (other is int) { + if (_log != null) { + _log("=$other;"); + } + return (this.value - other).abs() <= tolerance; + } + if (other is double) { + if (_log != null) { + _log("=${other.toStringAsFixed(2)};"); + } + return (this.value - other).abs() <= tolerance; + } + return false; + } + + @override + String toString() => "Area($value)"; +} + +class Shape { + String _log = ""; + Shape(); + + void logger(String toAppend) { + _log = _log.isEmpty ? toAppend : "$_log$toAppend"; + } + + Area get area { + logger("Shape.area"); + return Area.withLogger(0, logger); + } + + String get log => _log; +} + +class Square extends Shape { + final double length; + + Square(this.length); + + Area get area { + logger("Square.area"); + return Area.withLogger(length * length, logger); + } +} + +class Circle extends Shape { + final double radius; + + Circle(this.radius); + + Area get area { + logger("Circle.area"); + return Area.withLogger(3.14 * radius * radius, logger); + } +} + +class Rectangle extends Shape { + final double x, y; + + Rectangle(this.x, this.y); + + Area get area { + logger("Rectangle.area"); + return Area.withLogger(x * y, logger); + } +}