| // 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('|'); | |
| } |