1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-03 08:19:13 +00:00

Add boolean parse

Closes https://github.com/dart-lang/sdk/pull/51026

Co-authored-by: Renato Burton <renatoburton96@gmail.com>
GitOrigin-RevId: e85a56ce338476b38eac890fac2b8ca193ca42e8
Change-Id: I60f92c594830ef0438ecd92b4c83cec609054326
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279746
Reviewed-by: Lasse Nielsen <lrn@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Lasse Nielsen <lrn@google.com>
This commit is contained in:
Lasse R.H. Nielsen 2023-03-09 15:14:32 +00:00 committed by Commit Queue
parent e0fedb81a1
commit c974c70f31
10 changed files with 202 additions and 4 deletions

View File

@ -29,6 +29,7 @@
* `StringConversionSink`
#### `dart:core`
- Added `bool.parse` and `bool.tryParse` static methods.
- **Breaking change** [#49529][]:
- Removed the deprecated `List` constructor, as it wasn't null safe.

View File

@ -34,7 +34,9 @@ abstract class String implements Comparable<String> {
List<int> get codeUnits;
}
class bool extends Object {}
class bool extends Object {
static bool parse(String source, {bool caseSensitive = false}) => false;
}
abstract class num implements Comparable<num> {
bool operator <(num other);

View File

@ -703,6 +703,16 @@ class bool {
'bool.hasEnvironment can only be used as a const constructor');
}
@patch
static bool parse(String source, {bool caseSensitive = true}) =>
Primitives.parseBool(source, caseSensitive) ??
(throw FormatException("Invalid boolean", source));
@patch
static bool? tryParse(String source, {bool caseSensitive = true}) {
return Primitives.parseBool(source, caseSensitive);
}
@patch
int get hashCode => super.hashCode;

View File

@ -159,6 +159,31 @@ class Primitives {
return result;
}
static bool? parseBool(
@nullCheck String source, @nullCheck bool caseSensitive) {
if (caseSensitive) {
return JS('bool', r'# == "true" || # != "false" && null', source, source);
}
return _compareIgnoreCase(source, "true")
? true
: _compareIgnoreCase(source, "false")
? false
: null;
}
/// Compares a string against an ASCII lower-case letter-only string.
///
/// Returns `true` if the [input] has the same length and same letters
/// as [lowerCaseTarget], `false` if not.
static bool _compareIgnoreCase(String input, String lowerCaseTarget) {
if (input.length != lowerCaseTarget.length) return false;
var delta = 0x20;
for (var i = 0; i < input.length; i++) {
delta |= input.codeUnitAt(i) ^ lowerCaseTarget.codeUnitAt(i);
}
return delta == 0x20;
}
/** `r"$".codeUnitAt(0)` */
static const int DOLLAR_CHAR_VALUE = 36;

View File

@ -580,6 +580,16 @@ class String {
class bool {
@patch
int get hashCode => super.hashCode;
@patch
static bool parse(String source, {bool caseSensitive = true}) =>
tryParse(source, caseSensitive: caseSensitive) ??
(throw FormatException("Invalid boolean", source));
@patch
static bool? tryParse(String source, {bool caseSensitive = true}) {
return Primitives.parseBool(source, caseSensitive);
}
}
@patch

View File

@ -388,7 +388,6 @@ class Primitives {
static bool? parseBool(String source, bool caseSensitive) {
checkNotNullable(source, "source");
checkNotNullable(caseSensitive, "caseSensitive");
// The caseSensitive defaults to true.
if (caseSensitive) {
return source == "true"
? true
@ -396,7 +395,6 @@ class Primitives {
? false
: null;
}
// Compare case-sensitive when caseSensitive is false.
return _compareIgnoreCase(source, "true")
? true
: _compareIgnoreCase(source, "false")

View File

@ -2,7 +2,7 @@
// 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 "dart:_internal" show patch;
import "dart:_internal" show patch, checkNotNullable;
@patch
@pragma("vm:entry-point")
@ -20,4 +20,50 @@ class bool {
int get hashCode => this ? 1231 : 1237;
int get _identityHashCode => this ? 1231 : 1237;
@patch
static bool parse(String source, {bool caseSensitive = true}) {
checkNotNullable(source, "source");
checkNotNullable(caseSensitive, "caseSensitive");
if (caseSensitive) {
return source == "true" ||
source != "false" &&
(throw FormatException("Invalid boolean", source));
}
// Ignore case-sensitive when `caseSensitive` is false.
return _compareIgnoreCase(source, "true") ||
!_compareIgnoreCase(source, "false") &&
(throw FormatException("Invalid boolean", source));
}
@patch
static bool? tryParse(String source, {bool caseSensitive = true}) {
checkNotNullable(source, "source");
checkNotNullable(caseSensitive, "caseSensitive");
if (caseSensitive) {
return source == "true"
? true
: source == "false"
? false
: null;
}
return _compareIgnoreCase(source, "true")
? true
: _compareIgnoreCase(source, "false")
? false
: null;
}
/// Compares a string against an ASCII lower-case letter-only string.
///
/// Returns `true` if the [input] has the same length and same letters
/// as [lowerCaseTarget], `false` if not.
static bool _compareIgnoreCase(String input, String lowerCaseTarget) {
if (input.length != lowerCaseTarget.length) return false;
var delta = 0x20;
for (var i = 0; i < input.length; i++) {
delta |= input.codeUnitAt(i) ^ lowerCaseTarget.codeUnitAt(i);
}
return delta == 0x20;
}
}

View File

@ -94,6 +94,68 @@ final class bool {
//ignore: const_factory
external const factory bool.hasEnvironment(String name);
/// Parses [source] as an, optionally case-insensitive, boolean literal.
///
/// If [caseSensitive] is `true`, which is the default,
/// the only accepted inputs are the strings `"true"` and `"false"`,
/// which returns the results `true` and `false` respectively.
///
/// If [caseSensitive] is `false`, any combination of upper and lower case
/// ASCII letters in the words `"true"` and `"false"` are accepted,
/// as if the input was first lower-cased.
///
/// Throws a [FormatException] if the [source] string does not contain
/// a valid boolean literal.
///
/// Rather than throwing and immediately catching the [FormatException],
/// instead use [tryParse] to handle a potential parsing error.
///
/// Example:
/// ```dart
/// print(bool.tryParse('true')); // true
/// print(bool.tryParse('false')); // false
/// print(bool.tryParse('TRUE')); // throws FormatException
/// print(bool.tryParse('TRUE', caseSensitive: false)); // true
/// print(bool.tryParse('FALSE', caseSensitive: false)); // false
/// print(bool.tryParse('NO')); // throws FormatException
/// print(bool.tryParse('YES')); // throws FormatException
/// print(bool.tryParse('0')); // throws FormatException
/// print(bool.tryParse('1')); // throws FormatException
/// ```
@Since("3.0")
external static bool parse(String source, {bool caseSensitive = true});
/// Parses [source] as an, optionally case-insensitive, boolean literal.
///
/// If [caseSensitive] is `true`, which is the default,
/// the only accepted inputs are the strings `"true"` and `"false"`,
/// which returns the results `true` and `false` respectively.
///
/// If [caseSensitive] is `false`, any combination of upper and lower case
/// ASCII letters in the words `"true"` and `"false"` are accepted,
/// as if the input was first lower-cased.
///
/// Returns `null` if the [source] string does not contain a valid
/// boolean literal.
///
/// If the input can be assumed to be valid, use [bool.parse] to avoid
/// having to deal with a possible `null` result.
///
/// Example:
/// ```dart
/// print(bool.tryParse('true')); // true
/// print(bool.tryParse('false')); // false
/// print(bool.tryParse('TRUE')); // null
/// print(bool.tryParse('TRUE', caseSensitive: false)); // true
/// print(bool.tryParse('FALSE', caseSensitive: false)); // false
/// print(bool.tryParse('NO')); // null
/// print(bool.tryParse('YES')); // null
/// print(bool.tryParse('0')); // null
/// print(bool.tryParse('1')); // null
/// ```
@Since("3.0")
external static bool? tryParse(String source, {bool caseSensitive = true});
external int get hashCode;
/// The logical conjunction ("and") of this and [other].

View File

@ -0,0 +1,23 @@
// 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.
// SharedOptions=-Da=true -Db=false -Dc=NOTBOOL -Dd=True
import "package:expect/expect.dart";
main() {
Expect.isTrue(bool.parse('true'));
Expect.isFalse(bool.parse('false'));
Expect.isTrue(bool.parse('TRUE', caseSensitive: false));
Expect.isFalse(bool.parse('FALSE', caseSensitive: false));
Expect.isTrue(bool.parse('true', caseSensitive: true));
Expect.isFalse(bool.parse('false', caseSensitive: true));
Expect.throws(() => bool.parse('True'));
Expect.throws(() => bool.parse('False'));
Expect.throws(() => bool.parse('y'));
Expect.throws(() => bool.parse('n'));
Expect.throws(() => bool.parse('0'));
Expect.throws(() => bool.parse('1'));
Expect.throws(() => bool.parse('TRUE', caseSensitive: true));
Expect.throws(() => bool.parse('FALSE', caseSensitive: true));
}

View File

@ -0,0 +1,21 @@
// 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.
// SharedOptions=-Da=true -Db=false -Dc=NOTBOOL -Dd=True
import "package:expect/expect.dart";
main() {
Expect.isTrue(bool.tryParse('true'));
Expect.isFalse(bool.tryParse('false'));
Expect.isTrue(bool.tryParse('TRUE', caseSensitive: false));
Expect.isFalse(bool.tryParse('FALSE', caseSensitive: false));
Expect.isNull(bool.tryParse('TRUE'));
Expect.isNull(bool.tryParse('FALSE'));
Expect.isNull(bool.tryParse('y'));
Expect.isNull(bool.tryParse('n'));
Expect.isNull(bool.tryParse(' true ', caseSensitive: false));
Expect.isNull(bool.tryParse(' false ', caseSensitive: false));
Expect.isNull(bool.tryParse('0', caseSensitive: true));
Expect.isNull(bool.tryParse('1', caseSensitive: true));
}