Skip to content

Commit

Permalink
Add @useResult to parser constructors to avoid bugs when using the …
Browse files Browse the repository at this point in the history
…old parser instance.
  • Loading branch information
renggli committed Oct 30, 2022
1 parent 3dd5190 commit 8e141f3
Show file tree
Hide file tree
Showing 55 changed files with 165 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 5.2.0 (unpublished)

- Add `@useResult` to parser constructors to avoid bugs when using the old parser instance.

## 5.1.0

- Dart 2.18 requirement.
Expand Down
1 change: 1 addition & 0 deletions bin/generate_sequence.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Future<void> generateImplementation(int index) async {
out.writeln('/// Creates a parser that consumes a sequence of $index parsers '
'and returns a ');
out.writeln('/// typed sequence [Sequence$index].');
out.writeln('@useResult');
out.writeln('Parser<Sequence$index<${resultTypes.join(', ')}>> '
'seq$index<${resultTypes.join(', ')}>(');
for (var i = 0; i < index; i++) {
Expand Down
2 changes: 2 additions & 0 deletions lib/src/context/context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ class Context {
/// Returns a result indicating a parse success.
@inlineVm
@inlineJs
@useResult
Success<R> success<R>(R result, [int? position]) =>
Success<R>(buffer, position ?? this.position, result);

/// Returns a result indicating a parse failure.
@inlineVm
@inlineJs
@useResult
Failure<R> failure<R>(String message, [int? position]) =>
Failure<R>(buffer, position ?? this.position, message);

Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/action/cast.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import 'package:meta/meta.dart';

import '../../context/context.dart';
import '../../context/result.dart';
import '../../core/parser.dart';
import '../combinator/delegate.dart';

extension CastParserExtension<T> on Parser<T> {
/// Returns a parser that casts itself to `Parser<R>`.
@useResult
Parser<R> cast<R>() => CastParser<T, R>(this);
}

Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/action/cast_list.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:meta/meta.dart';

import '../../context/context.dart';
import '../../context/result.dart';
import '../../core/parser.dart';
Expand All @@ -6,6 +8,7 @@ import '../combinator/delegate.dart';
extension CastListParserExtension<T> on Parser<T> {
/// Returns a parser that casts itself to `Parser<List<R>>`. Assumes this
/// parser to be of type `Parser<List>`.
@useResult
Parser<List<R>> castList<R>() => CastListParser<T, R>(this);
}

Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/action/continuation.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:meta/meta.dart';

import '../../context/context.dart';
import '../../context/result.dart';
import '../../core/parser.dart';
Expand Down Expand Up @@ -29,6 +31,7 @@ extension ContinuationParserExtension<T> on Parser<T> {
/// return result;
/// });
///
@useResult
Parser<R> callCC<R>(ContinuationHandler<T, R> handler) =>
ContinuationParser<T, R>(this, handler);
}
Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/action/flatten.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:meta/meta.dart';

import '../../context/context.dart';
import '../../context/result.dart';
import '../../core/parser.dart';
Expand All @@ -14,6 +16,7 @@ extension FlattenParserExtension<T> on Parser<T> {
/// For example, the parser `letter().plus().flatten()` returns `'abc'`
/// for the input `'abc'`. In contrast, the parser `letter().plus()` would
/// return `['a', 'b', 'c']` for the same input instead.
@useResult
Parser<String> flatten([String? message]) => FlattenParser<T>(this, message);
}

Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/action/map.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:meta/meta.dart';

import '../../context/context.dart';
import '../../context/result.dart';
import '../../core/parser.dart';
Expand All @@ -11,6 +13,7 @@ extension MapParserExtension<T> on Parser<T> {
/// For example, the parser `digit().map((char) => int.parse(char))` returns
/// the number `1` for the input string `'1'`. If the delegate fails, the
/// production action is not executed and the failure is passed on.
@useResult
Parser<R> map<R>(
Callback<T, R> callback, {
@Deprecated('All callbacks are considered to have side-effects.')
Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/action/permute.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:meta/meta.dart';

import '../../context/context.dart';
import '../../context/result.dart';
import '../../core/parser.dart';
Expand All @@ -11,6 +13,7 @@ extension PermuteParserExtension<T> on Parser<List<T>> {
/// For example, the parser `letter().star().permute([0, -1])` returns the
/// first and last letter parsed. For the input `'abc'` it returns
/// `['a', 'c']`.
@useResult
Parser<List<T>> permute(List<int> indexes) => PermuteParser<T>(this, indexes);
}

Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/action/pick.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:meta/meta.dart';

import '../../context/context.dart';
import '../../context/result.dart';
import '../../core/parser.dart';
Expand All @@ -10,6 +12,7 @@ extension PickParserExtension<T> on Parser<List<T>> {
///
/// For example, the parser `letter().star().pick(-1)` returns the last
/// letter parsed. For the input `'abc'` it returns `'c'`.
@useResult
Parser<T> pick(int index) => PickParser<T>(this, index);
}

Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/action/token.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:meta/meta.dart';

import '../../context/context.dart';
import '../../context/result.dart';
import '../../core/parser.dart';
Expand All @@ -12,6 +14,7 @@ extension TokenParserExtension<T> on Parser<T> {
///
/// For example, the parser `letter().plus().token()` returns the token
/// `Token[start: 0, stop: 3, value: abc]` for the input `'abc'`.
@useResult
Parser<Token<T>> token() => TokenParser<T>(this);
}

Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/action/trimming.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:meta/meta.dart';

import '../../context/context.dart';
import '../../context/result.dart';
import '../../core/parser.dart';
Expand All @@ -15,6 +17,7 @@ extension TrimmingParserExtension<T> on Parser<T> {
///
/// For example, the parser `letter().plus().trim()` returns `['a', 'b']`
/// for the input `' ab\n'` and consumes the complete input string.
@useResult
Parser<T> trim([Parser<void>? left, Parser<void>? right]) =>
TrimmingParser<T>(this, left ??= whitespace(), right ??= left);
}
Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/action/where.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:meta/meta.dart';

import '../../context/context.dart';
import '../../context/result.dart';
import '../../core/parser.dart';
Expand Down Expand Up @@ -29,6 +31,7 @@ extension WhereParserExtension<T> on Parser<T> {
/// parser.parse('aa'); // ==> Success: ['a', 'a']
/// parser.parse('ab'); // ==> Failure: characters do not match
///
@useResult
Parser<T> where(Predicate<T> predicate,
{Callback<T, String>? failureMessage,
Callback<T, int>? failurePosition}) =>
Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/character/any_of.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import 'package:meta/meta.dart';

import '../../core/parser.dart';
import 'code.dart';
import 'optimize.dart';
import 'parser.dart';

/// Returns a parser that accepts any of the specified characters.
@useResult
Parser<String> anyOf(String chars, [String? message]) => CharacterParser(
optimizedString(chars),
message ?? 'any of "${toReadableString(chars)}" expected');
4 changes: 4 additions & 0 deletions lib/src/parser/character/char.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:meta/meta.dart';

import '../../core/parser.dart';
import 'code.dart';
import 'optimize.dart';
Expand All @@ -6,11 +8,13 @@ import 'predicate.dart';
import 'range.dart';

/// Returns a parser that accepts a specific character only.
@useResult
Parser<String> char(String char, [String? message]) => CharacterParser(
SingleCharPredicate(toCharCode(char)),
message ?? '"${toReadableString(char)}" expected');

/// Returns a parser that accepts a case-insensitive specific character only.
@useResult
Parser<String> charIgnoringCase(String char, [String? message]) {
final lowerCase = toCharCode(char.toLowerCase());
final upperCase = toCharCode(char.toUpperCase());
Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/character/digit.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import 'package:meta/meta.dart';

import '../../core/parser.dart';
import 'parser.dart';
import 'predicate.dart';

/// Returns a parser that accepts any digit character. The accepted input is
/// equivalent to the character-set `0-9`.
@useResult
Parser<String> digit([String message = 'digit expected']) =>
CharacterParser(const DigitCharPredicate(), message);

Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/character/letter.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import 'package:meta/meta.dart';

import '../../core/parser.dart';
import 'parser.dart';
import 'predicate.dart';

/// Returns a parser that accepts any letter character (lowercase or uppercase).
/// The accepted input is equivalent to the character-set `a-zA-Z`.
@useResult
Parser<String> letter([String message = 'letter expected']) =>
CharacterParser(const LetterCharPredicate(), message);

Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/character/lowercase.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import 'package:meta/meta.dart';

import '../../core/parser.dart';
import 'parser.dart';
import 'predicate.dart';

/// Returns a parser that accepts any lowercase character. The accepted input is
/// equivalent to the character-set `a-z`.
@useResult
Parser<String> lowercase([String message = 'lowercase letter expected']) =>
CharacterParser(const LowercaseCharPredicate(), message);

Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/character/none_of.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import 'package:meta/meta.dart';

import '../../core/parser.dart';
import 'code.dart';
import 'not.dart';
import 'optimize.dart';
import 'parser.dart';

/// Returns a parser that accepts none of the specified characters.
@useResult
Parser<String> noneOf(String chars, [String? message]) => CharacterParser(
NotCharacterPredicate(optimizedString(chars)),
message ?? 'none of "${toReadableString(chars)}" expected');
4 changes: 4 additions & 0 deletions lib/src/parser/character/pattern.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:meta/meta.dart';

import '../../core/parser.dart';
import '../action/map.dart';
import '../combinator/choice.dart';
Expand All @@ -23,6 +25,7 @@ import 'range.dart';
/// either '1', '2', or '3'; and fails for any other character. The parser
/// `pattern('^aou') accepts any character, but fails for the characters 'a',
/// 'o', or 'u'.
@useResult
Parser<String> pattern(String element, [String? message]) => CharacterParser(
_pattern.parse(element).value,
message ?? '[${toReadableString(element)}] expected');
Expand All @@ -38,6 +41,7 @@ Parser<String> pattern(String element, [String? message]) => CharacterParser(
/// `patternIgnoreCase('a-c')` accepts 'a', 'b', 'c' and 'A', 'B', 'C'; and
/// fails for any other character. The parser `patternIgnoreCase('^A') accepts
/// any character, but fails for the characters 'a' or 'A'.
@useResult
Parser<String> patternIgnoreCase(String element, [String? message]) {
var normalized = element;
final isNegated = normalized.startsWith('^');
Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/character/range.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import 'package:meta/meta.dart';

import '../../core/parser.dart';
import 'code.dart';
import 'parser.dart';
import 'predicate.dart';

/// Returns a parser that accepts any character in the range
/// between [start] and [stop].
@useResult
Parser<String> range(String start, String stop, [String? message]) =>
CharacterParser(
RangeCharPredicate(toCharCode(start), toCharCode(stop)),
Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/character/uppercase.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import 'package:meta/meta.dart';

import '../../core/parser.dart';
import 'parser.dart';
import 'predicate.dart';

/// Returns a parser that accepts any uppercase character. The accepted input is
/// equivalent to the character-set `A-Z`.
@useResult
Parser<String> uppercase([String message = 'uppercase letter expected']) =>
CharacterParser(const UppercaseCharPredicate(), message);

Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/character/whitespace.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import 'package:meta/meta.dart';

import '../../core/parser.dart';
import 'parser.dart';
import 'predicate.dart';

/// Returns a parser that accepts any whitespace character.
@useResult
Parser<String> whitespace([String message = 'whitespace expected']) =>
CharacterParser(const WhitespaceCharPredicate(), message);

Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/character/word.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import 'package:meta/meta.dart';

import '../../core/parser.dart';
import 'parser.dart';
import 'predicate.dart';

/// Returns a parser that accepts any word character (lowercase, uppercase,
/// underscore, or digit). The accepted input is equivalent to the character-set
/// `a-zA-Z_0-9`.
@useResult
Parser<String> word([String message = 'letter or digit expected']) =>
CharacterParser(const WordCharPredicate(), message);

Expand Down
3 changes: 3 additions & 0 deletions lib/src/parser/combinator/and.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:meta/meta.dart';

import '../../context/context.dart';
import '../../context/result.dart';
import '../../core/parser.dart';
Expand All @@ -11,6 +13,7 @@ extension AndParserExtension<T> on Parser<T> {
/// identifiers that start with an underscore character. Since the predicate
/// does not consume accepted input, the parser `identifier` is given the
/// ability to process the complete identifier.
@useResult
Parser<T> and() => AndParser<T>(this);
}

Expand Down
4 changes: 4 additions & 0 deletions lib/src/parser/combinator/choice.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:meta/meta.dart';

import '../../context/context.dart';
import '../../context/failure.dart';
import '../../context/result.dart';
Expand Down Expand Up @@ -25,6 +27,7 @@ extension ChoiceParserExtension on Parser {
/// Due to https://github.com/dart-lang/language/issues/1557 the resulting
/// parser cannot be properly typed. Please use [ChoiceIterableExtension]
/// as a workaround: `[first, second].toChoiceParser()`.
@useResult
ChoiceParser or(Parser other, {FailureJoiner? failureJoiner}) {
final self = this;
return self is ChoiceParser
Expand All @@ -35,6 +38,7 @@ extension ChoiceParserExtension on Parser {

/// Convenience operator returning a parser that accepts the receiver or
/// [other]. See [or] for details.
@useResult
ChoiceParser operator |(Parser other) => or(other);
}

Expand Down
1 change: 1 addition & 0 deletions lib/src/parser/combinator/generated/sequence_2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import '../../utils/sequential.dart';

/// Creates a parser that consumes a sequence of 2 parsers and returns a
/// typed sequence [Sequence2].
@useResult
Parser<Sequence2<R1, R2>> seq2<R1, R2>(
Parser<R1> parser1,
Parser<R2> parser2,
Expand Down
1 change: 1 addition & 0 deletions lib/src/parser/combinator/generated/sequence_3.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import '../../utils/sequential.dart';

/// Creates a parser that consumes a sequence of 3 parsers and returns a
/// typed sequence [Sequence3].
@useResult
Parser<Sequence3<R1, R2, R3>> seq3<R1, R2, R3>(
Parser<R1> parser1,
Parser<R2> parser2,
Expand Down
1 change: 1 addition & 0 deletions lib/src/parser/combinator/generated/sequence_4.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import '../../utils/sequential.dart';

/// Creates a parser that consumes a sequence of 4 parsers and returns a
/// typed sequence [Sequence4].
@useResult
Parser<Sequence4<R1, R2, R3, R4>> seq4<R1, R2, R3, R4>(
Parser<R1> parser1,
Parser<R2> parser2,
Expand Down
Loading

0 comments on commit 8e141f3

Please sign in to comment.