| // Copyright (c) 2023, 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. |
| |
| import 'key.dart'; |
| import 'path.dart'; |
| import 'static_type.dart'; |
| |
| /// The main pattern for matching types and destructuring. |
| /// |
| /// It has a type which determines the type of values it contains. The type may |
| /// be [StaticType.nullableObject] to indicate that it doesn't filter by type. |
| /// |
| /// It may also contain zero or more named properties. The pattern then only |
| /// matches values where the property values are matched by the corresponding |
| /// property patterns. |
| class SingleSpace { |
| static final SingleSpace empty = new SingleSpace(StaticType.neverType); |
| |
| /// The type of values the pattern matches. |
| final StaticType type; |
| |
| /// Any property subpatterns the pattern matches. |
| final Map<Key, Space> properties; |
| |
| /// Additional properties for map/list semantics. |
| final Map<Key, Space> additionalProperties; |
| |
| SingleSpace( |
| this.type, { |
| this.properties = const {}, |
| this.additionalProperties = const {}, |
| }); |
| |
| @override |
| late final int hashCode = Object.hash( |
| type, |
| Object.hashAllUnordered(properties.keys), |
| Object.hashAllUnordered(properties.values), |
| ); |
| |
| @override |
| bool operator ==(Object other) { |
| if (identical(this, other)) return true; |
| if (other is! SingleSpace) return false; |
| if (type != other.type) return false; |
| if (properties.length != other.properties.length) return false; |
| if (properties.isNotEmpty) { |
| for (MapEntry<Key, Space> entry in properties.entries) { |
| if (entry.value != other.properties[entry.key]) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| @override |
| String toString() { |
| return type.spaceToText(properties, additionalProperties); |
| } |
| } |
| |
| /// A set of runtime values encoded as a union of [SingleSpace]s. |
| /// |
| /// This is used to support logical-or patterns without having to eagerly |
| /// expand the subpatterns in the parent context. |
| class Space { |
| /// The path of getters that led from the original matched value to value |
| /// matched by this pattern. Used to generate a human-readable witness. |
| final Path path; |
| |
| final List<SingleSpace> singleSpaces; |
| |
| /// Create an empty space. |
| Space.empty(this.path) : singleSpaces = [SingleSpace.empty]; |
| |
| Space( |
| Path path, |
| StaticType type, { |
| Map<Key, Space> properties = const {}, |
| Map<Key, Space> additionalProperties = const {}, |
| }) : this._(path, [ |
| new SingleSpace( |
| type, |
| properties: properties, |
| additionalProperties: additionalProperties, |
| ), |
| ]); |
| |
| Space._(this.path, this.singleSpaces); |
| |
| factory Space.fromSingleSpaces(Path path, List<SingleSpace> singleSpaces) { |
| Set<SingleSpace> singleSpacesSet = {}; |
| |
| for (SingleSpace singleSpace in singleSpaces) { |
| // Discard empty space. |
| if (singleSpace == SingleSpace.empty) { |
| continue; |
| } |
| |
| singleSpacesSet.add(singleSpace); |
| } |
| |
| List<SingleSpace> singleSpacesList = singleSpacesSet.toList(); |
| if (singleSpacesSet.isEmpty) { |
| singleSpacesList.add(SingleSpace.empty); |
| } else if (singleSpacesList.length == 2) { |
| if (singleSpacesList[0].type == StaticType.nullType && |
| singleSpacesList[0].properties.isEmpty && |
| singleSpacesList[1].properties.isEmpty) { |
| singleSpacesList = [new SingleSpace(singleSpacesList[1].type.nullable)]; |
| } else if (singleSpacesList[1].type == StaticType.nullType && |
| singleSpacesList[1].properties.isEmpty && |
| singleSpacesList[0].properties.isEmpty) { |
| singleSpacesList = [new SingleSpace(singleSpacesList[0].type.nullable)]; |
| } |
| } |
| return new Space._(path, singleSpacesList); |
| } |
| |
| Space union(Space other) { |
| return new Space.fromSingleSpaces(path, [ |
| ...singleSpaces, |
| ...other.singleSpaces, |
| ]); |
| } |
| |
| @override |
| String toString() => singleSpaces.join('|'); |
| } |