blob: 7207ba701bdfec61339749da4e3c6b19c00b9e09 [file] [log] [blame]
// 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('|');
}