Skip to content

Commit c974c70

Browse files
lrhnsrburton
authored and
Commit Queue
committed
Add boolean parse
Closes #51026 Co-authored-by: Renato Burton <[email protected]> GitOrigin-RevId: e85a56c Change-Id: I60f92c594830ef0438ecd92b4c83cec609054326 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/279746 Reviewed-by: Lasse Nielsen <[email protected]> Reviewed-by: Sigmund Cherem <[email protected]> Commit-Queue: Lasse Nielsen <[email protected]>
1 parent e0fedb8 commit c974c70

File tree

10 files changed

+202
-4
lines changed

10 files changed

+202
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
* `StringConversionSink`
3030

3131
#### `dart:core`
32+
- Added `bool.parse` and `bool.tryParse` static methods.
3233

3334
- **Breaking change** [#49529][]:
3435
- Removed the deprecated `List` constructor, as it wasn't null safe.

pkg/analyzer_cli/test/data/sky_engine/lib/core.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ abstract class String implements Comparable<String> {
3434
List<int> get codeUnits;
3535
}
3636

37-
class bool extends Object {}
37+
class bool extends Object {
38+
static bool parse(String source, {bool caseSensitive = false}) => false;
39+
}
3840

3941
abstract class num implements Comparable<num> {
4042
bool operator <(num other);

sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,16 @@ class bool {
703703
'bool.hasEnvironment can only be used as a const constructor');
704704
}
705705

706+
@patch
707+
static bool parse(String source, {bool caseSensitive = true}) =>
708+
Primitives.parseBool(source, caseSensitive) ??
709+
(throw FormatException("Invalid boolean", source));
710+
711+
@patch
712+
static bool? tryParse(String source, {bool caseSensitive = true}) {
713+
return Primitives.parseBool(source, caseSensitive);
714+
}
715+
706716
@patch
707717
int get hashCode => super.hashCode;
708718

sdk/lib/_internal/js_dev_runtime/private/js_helper.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,31 @@ class Primitives {
159159
return result;
160160
}
161161

162+
static bool? parseBool(
163+
@nullCheck String source, @nullCheck bool caseSensitive) {
164+
if (caseSensitive) {
165+
return JS('bool', r'# == "true" || # != "false" && null', source, source);
166+
}
167+
return _compareIgnoreCase(source, "true")
168+
? true
169+
: _compareIgnoreCase(source, "false")
170+
? false
171+
: null;
172+
}
173+
174+
/// Compares a string against an ASCII lower-case letter-only string.
175+
///
176+
/// Returns `true` if the [input] has the same length and same letters
177+
/// as [lowerCaseTarget], `false` if not.
178+
static bool _compareIgnoreCase(String input, String lowerCaseTarget) {
179+
if (input.length != lowerCaseTarget.length) return false;
180+
var delta = 0x20;
181+
for (var i = 0; i < input.length; i++) {
182+
delta |= input.codeUnitAt(i) ^ lowerCaseTarget.codeUnitAt(i);
183+
}
184+
return delta == 0x20;
185+
}
186+
162187
/** `r"$".codeUnitAt(0)` */
163188
static const int DOLLAR_CHAR_VALUE = 36;
164189

sdk/lib/_internal/js_runtime/lib/core_patch.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,16 @@ class String {
580580
class bool {
581581
@patch
582582
int get hashCode => super.hashCode;
583+
584+
@patch
585+
static bool parse(String source, {bool caseSensitive = true}) =>
586+
tryParse(source, caseSensitive: caseSensitive) ??
587+
(throw FormatException("Invalid boolean", source));
588+
589+
@patch
590+
static bool? tryParse(String source, {bool caseSensitive = true}) {
591+
return Primitives.parseBool(source, caseSensitive);
592+
}
583593
}
584594

585595
@patch

sdk/lib/_internal/js_runtime/lib/js_helper.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,15 +388,13 @@ class Primitives {
388388
static bool? parseBool(String source, bool caseSensitive) {
389389
checkNotNullable(source, "source");
390390
checkNotNullable(caseSensitive, "caseSensitive");
391-
// The caseSensitive defaults to true.
392391
if (caseSensitive) {
393392
return source == "true"
394393
? true
395394
: source == "false"
396395
? false
397396
: null;
398397
}
399-
// Compare case-sensitive when caseSensitive is false.
400398
return _compareIgnoreCase(source, "true")
401399
? true
402400
: _compareIgnoreCase(source, "false")

sdk/lib/_internal/vm_shared/lib/bool_patch.dart

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
import "dart:_internal" show patch;
5+
import "dart:_internal" show patch, checkNotNullable;
66

77
@patch
88
@pragma("vm:entry-point")
@@ -20,4 +20,50 @@ class bool {
2020
int get hashCode => this ? 1231 : 1237;
2121

2222
int get _identityHashCode => this ? 1231 : 1237;
23+
24+
@patch
25+
static bool parse(String source, {bool caseSensitive = true}) {
26+
checkNotNullable(source, "source");
27+
checkNotNullable(caseSensitive, "caseSensitive");
28+
if (caseSensitive) {
29+
return source == "true" ||
30+
source != "false" &&
31+
(throw FormatException("Invalid boolean", source));
32+
}
33+
// Ignore case-sensitive when `caseSensitive` is false.
34+
return _compareIgnoreCase(source, "true") ||
35+
!_compareIgnoreCase(source, "false") &&
36+
(throw FormatException("Invalid boolean", source));
37+
}
38+
39+
@patch
40+
static bool? tryParse(String source, {bool caseSensitive = true}) {
41+
checkNotNullable(source, "source");
42+
checkNotNullable(caseSensitive, "caseSensitive");
43+
if (caseSensitive) {
44+
return source == "true"
45+
? true
46+
: source == "false"
47+
? false
48+
: null;
49+
}
50+
return _compareIgnoreCase(source, "true")
51+
? true
52+
: _compareIgnoreCase(source, "false")
53+
? false
54+
: null;
55+
}
56+
57+
/// Compares a string against an ASCII lower-case letter-only string.
58+
///
59+
/// Returns `true` if the [input] has the same length and same letters
60+
/// as [lowerCaseTarget], `false` if not.
61+
static bool _compareIgnoreCase(String input, String lowerCaseTarget) {
62+
if (input.length != lowerCaseTarget.length) return false;
63+
var delta = 0x20;
64+
for (var i = 0; i < input.length; i++) {
65+
delta |= input.codeUnitAt(i) ^ lowerCaseTarget.codeUnitAt(i);
66+
}
67+
return delta == 0x20;
68+
}
2369
}

sdk/lib/core/bool.dart

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,68 @@ final class bool {
9494
//ignore: const_factory
9595
external const factory bool.hasEnvironment(String name);
9696

97+
/// Parses [source] as an, optionally case-insensitive, boolean literal.
98+
///
99+
/// If [caseSensitive] is `true`, which is the default,
100+
/// the only accepted inputs are the strings `"true"` and `"false"`,
101+
/// which returns the results `true` and `false` respectively.
102+
///
103+
/// If [caseSensitive] is `false`, any combination of upper and lower case
104+
/// ASCII letters in the words `"true"` and `"false"` are accepted,
105+
/// as if the input was first lower-cased.
106+
///
107+
/// Throws a [FormatException] if the [source] string does not contain
108+
/// a valid boolean literal.
109+
///
110+
/// Rather than throwing and immediately catching the [FormatException],
111+
/// instead use [tryParse] to handle a potential parsing error.
112+
///
113+
/// Example:
114+
/// ```dart
115+
/// print(bool.tryParse('true')); // true
116+
/// print(bool.tryParse('false')); // false
117+
/// print(bool.tryParse('TRUE')); // throws FormatException
118+
/// print(bool.tryParse('TRUE', caseSensitive: false)); // true
119+
/// print(bool.tryParse('FALSE', caseSensitive: false)); // false
120+
/// print(bool.tryParse('NO')); // throws FormatException
121+
/// print(bool.tryParse('YES')); // throws FormatException
122+
/// print(bool.tryParse('0')); // throws FormatException
123+
/// print(bool.tryParse('1')); // throws FormatException
124+
/// ```
125+
@Since("3.0")
126+
external static bool parse(String source, {bool caseSensitive = true});
127+
128+
/// Parses [source] as an, optionally case-insensitive, boolean literal.
129+
///
130+
/// If [caseSensitive] is `true`, which is the default,
131+
/// the only accepted inputs are the strings `"true"` and `"false"`,
132+
/// which returns the results `true` and `false` respectively.
133+
///
134+
/// If [caseSensitive] is `false`, any combination of upper and lower case
135+
/// ASCII letters in the words `"true"` and `"false"` are accepted,
136+
/// as if the input was first lower-cased.
137+
///
138+
/// Returns `null` if the [source] string does not contain a valid
139+
/// boolean literal.
140+
///
141+
/// If the input can be assumed to be valid, use [bool.parse] to avoid
142+
/// having to deal with a possible `null` result.
143+
///
144+
/// Example:
145+
/// ```dart
146+
/// print(bool.tryParse('true')); // true
147+
/// print(bool.tryParse('false')); // false
148+
/// print(bool.tryParse('TRUE')); // null
149+
/// print(bool.tryParse('TRUE', caseSensitive: false)); // true
150+
/// print(bool.tryParse('FALSE', caseSensitive: false)); // false
151+
/// print(bool.tryParse('NO')); // null
152+
/// print(bool.tryParse('YES')); // null
153+
/// print(bool.tryParse('0')); // null
154+
/// print(bool.tryParse('1')); // null
155+
/// ```
156+
@Since("3.0")
157+
external static bool? tryParse(String source, {bool caseSensitive = true});
158+
97159
external int get hashCode;
98160

99161
/// The logical conjunction ("and") of this and [other].

tests/corelib/bool_parse_test.dart

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
// SharedOptions=-Da=true -Db=false -Dc=NOTBOOL -Dd=True
5+
6+
import "package:expect/expect.dart";
7+
8+
main() {
9+
Expect.isTrue(bool.parse('true'));
10+
Expect.isFalse(bool.parse('false'));
11+
Expect.isTrue(bool.parse('TRUE', caseSensitive: false));
12+
Expect.isFalse(bool.parse('FALSE', caseSensitive: false));
13+
Expect.isTrue(bool.parse('true', caseSensitive: true));
14+
Expect.isFalse(bool.parse('false', caseSensitive: true));
15+
Expect.throws(() => bool.parse('True'));
16+
Expect.throws(() => bool.parse('False'));
17+
Expect.throws(() => bool.parse('y'));
18+
Expect.throws(() => bool.parse('n'));
19+
Expect.throws(() => bool.parse('0'));
20+
Expect.throws(() => bool.parse('1'));
21+
Expect.throws(() => bool.parse('TRUE', caseSensitive: true));
22+
Expect.throws(() => bool.parse('FALSE', caseSensitive: true));
23+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
// SharedOptions=-Da=true -Db=false -Dc=NOTBOOL -Dd=True
5+
6+
import "package:expect/expect.dart";
7+
8+
main() {
9+
Expect.isTrue(bool.tryParse('true'));
10+
Expect.isFalse(bool.tryParse('false'));
11+
Expect.isTrue(bool.tryParse('TRUE', caseSensitive: false));
12+
Expect.isFalse(bool.tryParse('FALSE', caseSensitive: false));
13+
Expect.isNull(bool.tryParse('TRUE'));
14+
Expect.isNull(bool.tryParse('FALSE'));
15+
Expect.isNull(bool.tryParse('y'));
16+
Expect.isNull(bool.tryParse('n'));
17+
Expect.isNull(bool.tryParse(' true ', caseSensitive: false));
18+
Expect.isNull(bool.tryParse(' false ', caseSensitive: false));
19+
Expect.isNull(bool.tryParse('0', caseSensitive: true));
20+
Expect.isNull(bool.tryParse('1', caseSensitive: true));
21+
}

0 commit comments

Comments
 (0)