diff --git a/CHANGELOG.md b/CHANGELOG.md index ba1c11b7..e34a9693 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/bin/generate_sequence.dart b/bin/generate_sequence.dart index 658ebff4..c2bad202 100644 --- a/bin/generate_sequence.dart +++ b/bin/generate_sequence.dart @@ -61,6 +61,7 @@ Future 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> ' 'seq$index<${resultTypes.join(', ')}>('); for (var i = 0; i < index; i++) { diff --git a/lib/src/context/context.dart b/lib/src/context/context.dart index 70a784c1..35f4456c 100644 --- a/lib/src/context/context.dart +++ b/lib/src/context/context.dart @@ -19,12 +19,14 @@ class Context { /// Returns a result indicating a parse success. @inlineVm @inlineJs + @useResult Success success(R result, [int? position]) => Success(buffer, position ?? this.position, result); /// Returns a result indicating a parse failure. @inlineVm @inlineJs + @useResult Failure failure(String message, [int? position]) => Failure(buffer, position ?? this.position, message); diff --git a/lib/src/parser/action/cast.dart b/lib/src/parser/action/cast.dart index 5b50fdb6..1f3cafc7 100644 --- a/lib/src/parser/action/cast.dart +++ b/lib/src/parser/action/cast.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -5,6 +7,7 @@ import '../combinator/delegate.dart'; extension CastParserExtension on Parser { /// Returns a parser that casts itself to `Parser`. + @useResult Parser cast() => CastParser(this); } diff --git a/lib/src/parser/action/cast_list.dart b/lib/src/parser/action/cast_list.dart index d12a084b..7bdbb756 100644 --- a/lib/src/parser/action/cast_list.dart +++ b/lib/src/parser/action/cast_list.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -6,6 +8,7 @@ import '../combinator/delegate.dart'; extension CastListParserExtension on Parser { /// Returns a parser that casts itself to `Parser>`. Assumes this /// parser to be of type `Parser`. + @useResult Parser> castList() => CastListParser(this); } diff --git a/lib/src/parser/action/continuation.dart b/lib/src/parser/action/continuation.dart index 39472e69..78f5b4a7 100644 --- a/lib/src/parser/action/continuation.dart +++ b/lib/src/parser/action/continuation.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -29,6 +31,7 @@ extension ContinuationParserExtension on Parser { /// return result; /// }); /// + @useResult Parser callCC(ContinuationHandler handler) => ContinuationParser(this, handler); } diff --git a/lib/src/parser/action/flatten.dart b/lib/src/parser/action/flatten.dart index 559d2fe5..f657e0ce 100644 --- a/lib/src/parser/action/flatten.dart +++ b/lib/src/parser/action/flatten.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -14,6 +16,7 @@ extension FlattenParserExtension on Parser { /// 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 flatten([String? message]) => FlattenParser(this, message); } diff --git a/lib/src/parser/action/map.dart b/lib/src/parser/action/map.dart index 919beade..cba1e1d9 100644 --- a/lib/src/parser/action/map.dart +++ b/lib/src/parser/action/map.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -11,6 +13,7 @@ extension MapParserExtension on Parser { /// 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 map( Callback callback, { @Deprecated('All callbacks are considered to have side-effects.') diff --git a/lib/src/parser/action/permute.dart b/lib/src/parser/action/permute.dart index dc1e205d..8e0e37e1 100644 --- a/lib/src/parser/action/permute.dart +++ b/lib/src/parser/action/permute.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -11,6 +13,7 @@ extension PermuteParserExtension on Parser> { /// 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> permute(List indexes) => PermuteParser(this, indexes); } diff --git a/lib/src/parser/action/pick.dart b/lib/src/parser/action/pick.dart index a455bc38..ad8bd019 100644 --- a/lib/src/parser/action/pick.dart +++ b/lib/src/parser/action/pick.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -10,6 +12,7 @@ extension PickParserExtension on Parser> { /// /// For example, the parser `letter().star().pick(-1)` returns the last /// letter parsed. For the input `'abc'` it returns `'c'`. + @useResult Parser pick(int index) => PickParser(this, index); } diff --git a/lib/src/parser/action/token.dart b/lib/src/parser/action/token.dart index daefe0bb..970506af 100644 --- a/lib/src/parser/action/token.dart +++ b/lib/src/parser/action/token.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -12,6 +14,7 @@ extension TokenParserExtension on Parser { /// /// For example, the parser `letter().plus().token()` returns the token /// `Token[start: 0, stop: 3, value: abc]` for the input `'abc'`. + @useResult Parser> token() => TokenParser(this); } diff --git a/lib/src/parser/action/trimming.dart b/lib/src/parser/action/trimming.dart index 66512dfd..dd410793 100644 --- a/lib/src/parser/action/trimming.dart +++ b/lib/src/parser/action/trimming.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -15,6 +17,7 @@ extension TrimmingParserExtension on Parser { /// /// For example, the parser `letter().plus().trim()` returns `['a', 'b']` /// for the input `' ab\n'` and consumes the complete input string. + @useResult Parser trim([Parser? left, Parser? right]) => TrimmingParser(this, left ??= whitespace(), right ??= left); } diff --git a/lib/src/parser/action/where.dart b/lib/src/parser/action/where.dart index 47685c7a..becd3ab0 100644 --- a/lib/src/parser/action/where.dart +++ b/lib/src/parser/action/where.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -29,6 +31,7 @@ extension WhereParserExtension on Parser { /// parser.parse('aa'); // ==> Success: ['a', 'a'] /// parser.parse('ab'); // ==> Failure: characters do not match /// + @useResult Parser where(Predicate predicate, {Callback? failureMessage, Callback? failurePosition}) => diff --git a/lib/src/parser/character/any_of.dart b/lib/src/parser/character/any_of.dart index 60e930a4..7426d100 100644 --- a/lib/src/parser/character/any_of.dart +++ b/lib/src/parser/character/any_of.dart @@ -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 anyOf(String chars, [String? message]) => CharacterParser( optimizedString(chars), message ?? 'any of "${toReadableString(chars)}" expected'); diff --git a/lib/src/parser/character/char.dart b/lib/src/parser/character/char.dart index 1ca41748..52565c6f 100644 --- a/lib/src/parser/character/char.dart +++ b/lib/src/parser/character/char.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../core/parser.dart'; import 'code.dart'; import 'optimize.dart'; @@ -6,11 +8,13 @@ import 'predicate.dart'; import 'range.dart'; /// Returns a parser that accepts a specific character only. +@useResult Parser 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 charIgnoringCase(String char, [String? message]) { final lowerCase = toCharCode(char.toLowerCase()); final upperCase = toCharCode(char.toUpperCase()); diff --git a/lib/src/parser/character/digit.dart b/lib/src/parser/character/digit.dart index 837f7e4b..16718d06 100644 --- a/lib/src/parser/character/digit.dart +++ b/lib/src/parser/character/digit.dart @@ -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 digit([String message = 'digit expected']) => CharacterParser(const DigitCharPredicate(), message); diff --git a/lib/src/parser/character/letter.dart b/lib/src/parser/character/letter.dart index 43de5ab7..cf7012e0 100644 --- a/lib/src/parser/character/letter.dart +++ b/lib/src/parser/character/letter.dart @@ -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 letter([String message = 'letter expected']) => CharacterParser(const LetterCharPredicate(), message); diff --git a/lib/src/parser/character/lowercase.dart b/lib/src/parser/character/lowercase.dart index e2ad68aa..c1015ca0 100644 --- a/lib/src/parser/character/lowercase.dart +++ b/lib/src/parser/character/lowercase.dart @@ -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 lowercase([String message = 'lowercase letter expected']) => CharacterParser(const LowercaseCharPredicate(), message); diff --git a/lib/src/parser/character/none_of.dart b/lib/src/parser/character/none_of.dart index 693d1be7..8a1681c7 100644 --- a/lib/src/parser/character/none_of.dart +++ b/lib/src/parser/character/none_of.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../core/parser.dart'; import 'code.dart'; import 'not.dart'; @@ -5,6 +7,7 @@ import 'optimize.dart'; import 'parser.dart'; /// Returns a parser that accepts none of the specified characters. +@useResult Parser noneOf(String chars, [String? message]) => CharacterParser( NotCharacterPredicate(optimizedString(chars)), message ?? 'none of "${toReadableString(chars)}" expected'); diff --git a/lib/src/parser/character/pattern.dart b/lib/src/parser/character/pattern.dart index 56c3be8f..eef9a94b 100644 --- a/lib/src/parser/character/pattern.dart +++ b/lib/src/parser/character/pattern.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../core/parser.dart'; import '../action/map.dart'; import '../combinator/choice.dart'; @@ -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 pattern(String element, [String? message]) => CharacterParser( _pattern.parse(element).value, message ?? '[${toReadableString(element)}] expected'); @@ -38,6 +41,7 @@ Parser 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 patternIgnoreCase(String element, [String? message]) { var normalized = element; final isNegated = normalized.startsWith('^'); diff --git a/lib/src/parser/character/range.dart b/lib/src/parser/character/range.dart index d758cee7..e7f8cc33 100644 --- a/lib/src/parser/character/range.dart +++ b/lib/src/parser/character/range.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../core/parser.dart'; import 'code.dart'; import 'parser.dart'; @@ -5,6 +7,7 @@ import 'predicate.dart'; /// Returns a parser that accepts any character in the range /// between [start] and [stop]. +@useResult Parser range(String start, String stop, [String? message]) => CharacterParser( RangeCharPredicate(toCharCode(start), toCharCode(stop)), diff --git a/lib/src/parser/character/uppercase.dart b/lib/src/parser/character/uppercase.dart index 2881c264..b3a52209 100644 --- a/lib/src/parser/character/uppercase.dart +++ b/lib/src/parser/character/uppercase.dart @@ -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 uppercase([String message = 'uppercase letter expected']) => CharacterParser(const UppercaseCharPredicate(), message); diff --git a/lib/src/parser/character/whitespace.dart b/lib/src/parser/character/whitespace.dart index baf3aa51..2a1af031 100644 --- a/lib/src/parser/character/whitespace.dart +++ b/lib/src/parser/character/whitespace.dart @@ -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 whitespace([String message = 'whitespace expected']) => CharacterParser(const WhitespaceCharPredicate(), message); diff --git a/lib/src/parser/character/word.dart b/lib/src/parser/character/word.dart index 618e4d5b..71ec4970 100644 --- a/lib/src/parser/character/word.dart +++ b/lib/src/parser/character/word.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../core/parser.dart'; import 'parser.dart'; import 'predicate.dart'; @@ -5,6 +7,7 @@ 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 word([String message = 'letter or digit expected']) => CharacterParser(const WordCharPredicate(), message); diff --git a/lib/src/parser/combinator/and.dart b/lib/src/parser/combinator/and.dart index fc11a3fb..3f1d53fa 100644 --- a/lib/src/parser/combinator/and.dart +++ b/lib/src/parser/combinator/and.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -11,6 +13,7 @@ extension AndParserExtension on Parser { /// 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 and() => AndParser(this); } diff --git a/lib/src/parser/combinator/choice.dart b/lib/src/parser/combinator/choice.dart index 03a032e0..51e5bf0b 100644 --- a/lib/src/parser/combinator/choice.dart +++ b/lib/src/parser/combinator/choice.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/failure.dart'; import '../../context/result.dart'; @@ -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 @@ -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); } diff --git a/lib/src/parser/combinator/generated/sequence_2.dart b/lib/src/parser/combinator/generated/sequence_2.dart index 68831135..eee77beb 100644 --- a/lib/src/parser/combinator/generated/sequence_2.dart +++ b/lib/src/parser/combinator/generated/sequence_2.dart @@ -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> seq2( Parser parser1, Parser parser2, diff --git a/lib/src/parser/combinator/generated/sequence_3.dart b/lib/src/parser/combinator/generated/sequence_3.dart index f1ad1f94..6703902e 100644 --- a/lib/src/parser/combinator/generated/sequence_3.dart +++ b/lib/src/parser/combinator/generated/sequence_3.dart @@ -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> seq3( Parser parser1, Parser parser2, diff --git a/lib/src/parser/combinator/generated/sequence_4.dart b/lib/src/parser/combinator/generated/sequence_4.dart index 64d561a0..a0be9253 100644 --- a/lib/src/parser/combinator/generated/sequence_4.dart +++ b/lib/src/parser/combinator/generated/sequence_4.dart @@ -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> seq4( Parser parser1, Parser parser2, diff --git a/lib/src/parser/combinator/generated/sequence_5.dart b/lib/src/parser/combinator/generated/sequence_5.dart index d53cb813..33ea0ac2 100644 --- a/lib/src/parser/combinator/generated/sequence_5.dart +++ b/lib/src/parser/combinator/generated/sequence_5.dart @@ -11,6 +11,7 @@ import '../../utils/sequential.dart'; /// Creates a parser that consumes a sequence of 5 parsers and returns a /// typed sequence [Sequence5]. +@useResult Parser> seq5( Parser parser1, Parser parser2, diff --git a/lib/src/parser/combinator/generated/sequence_6.dart b/lib/src/parser/combinator/generated/sequence_6.dart index 774f2281..593c24ef 100644 --- a/lib/src/parser/combinator/generated/sequence_6.dart +++ b/lib/src/parser/combinator/generated/sequence_6.dart @@ -11,6 +11,7 @@ import '../../utils/sequential.dart'; /// Creates a parser that consumes a sequence of 6 parsers and returns a /// typed sequence [Sequence6]. +@useResult Parser> seq6( Parser parser1, Parser parser2, diff --git a/lib/src/parser/combinator/generated/sequence_7.dart b/lib/src/parser/combinator/generated/sequence_7.dart index bc3fe8cf..f5edf643 100644 --- a/lib/src/parser/combinator/generated/sequence_7.dart +++ b/lib/src/parser/combinator/generated/sequence_7.dart @@ -11,6 +11,7 @@ import '../../utils/sequential.dart'; /// Creates a parser that consumes a sequence of 7 parsers and returns a /// typed sequence [Sequence7]. +@useResult Parser> seq7( Parser parser1, Parser parser2, diff --git a/lib/src/parser/combinator/generated/sequence_8.dart b/lib/src/parser/combinator/generated/sequence_8.dart index 78bbcfbe..a847bb82 100644 --- a/lib/src/parser/combinator/generated/sequence_8.dart +++ b/lib/src/parser/combinator/generated/sequence_8.dart @@ -11,6 +11,7 @@ import '../../utils/sequential.dart'; /// Creates a parser that consumes a sequence of 8 parsers and returns a /// typed sequence [Sequence8]. +@useResult Parser> seq8( Parser parser1, diff --git a/lib/src/parser/combinator/generated/sequence_9.dart b/lib/src/parser/combinator/generated/sequence_9.dart index 08da5da1..3886f910 100644 --- a/lib/src/parser/combinator/generated/sequence_9.dart +++ b/lib/src/parser/combinator/generated/sequence_9.dart @@ -11,6 +11,7 @@ import '../../utils/sequential.dart'; /// Creates a parser that consumes a sequence of 9 parsers and returns a /// typed sequence [Sequence9]. +@useResult Parser> seq9( Parser parser1, diff --git a/lib/src/parser/combinator/not.dart b/lib/src/parser/combinator/not.dart index b64946b0..48a0174d 100644 --- a/lib/src/parser/combinator/not.dart +++ b/lib/src/parser/combinator/not.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/failure.dart'; import '../../context/result.dart'; @@ -15,6 +17,7 @@ extension NotParserExtension on Parser { /// `char('_')` accepts the input, the negation and subsequently the /// complete parser fails. Otherwise the parser `identifier` is given the /// ability to process the complete identifier. + @useResult Parser> not([String message = 'success not expected']) => NotParser(this, message); @@ -24,6 +27,7 @@ extension NotParserExtension on Parser { /// For example, the parser `letter().neg()` accepts any input but a letter. /// The parser fails for inputs like `'a'` or `'Z'`, but succeeds for /// input like `'1'`, `'_'` or `'$'`. + @useResult Parser neg([String message = 'input not expected']) => seq2(not(message), any()).map2((_, value) => value); } diff --git a/lib/src/parser/combinator/optional.dart b/lib/src/parser/combinator/optional.dart index 524bd454..18bca6d7 100644 --- a/lib/src/parser/combinator/optional.dart +++ b/lib/src/parser/combinator/optional.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -10,6 +12,7 @@ extension OptionalParserExtension on Parser { /// For example, the parser `letter().optional()` accepts a letter as input /// and returns that letter. When given something else the parser succeeds as /// well, does not consume anything and returns `null`. + @useResult Parser optional() => OptionalParser(this, null); /// Returns new parser that accepts the receiver, if possible. The resulting @@ -18,6 +21,7 @@ extension OptionalParserExtension on Parser { /// For example, the parser `letter().optionalWith('!')` accepts a letter as /// input and returns that letter. When given something else the parser /// succeeds as well, does not consume anything and returns `'!'`. + @useResult Parser optionalWith(T value) => OptionalParser(this, value); } diff --git a/lib/src/parser/combinator/sequence.dart b/lib/src/parser/combinator/sequence.dart index 49706b76..b6190a8a 100644 --- a/lib/src/parser/combinator/sequence.dart +++ b/lib/src/parser/combinator/sequence.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -23,12 +25,14 @@ extension SequenceParserExtension on Parser { /// For example, the parser `letter().seq(digit()).seq(letter())` accepts a /// letter followed by a digit and another letter. The parse result of the /// input string `'a1b'` is the list `['a', '1', 'b']`. + @useResult Parser seq(Parser other) => this is SequenceParser ? SequenceParser([...children, other]) : SequenceParser([this, other]); /// Convenience operator returning a parser that accepts the receiver followed /// by [other]. See [seq] for details. + @useResult Parser operator &(Parser other) => seq(other); } diff --git a/lib/src/parser/combinator/settable.dart b/lib/src/parser/combinator/settable.dart index 7585d519..865f407c 100644 --- a/lib/src/parser/combinator/settable.dart +++ b/lib/src/parser/combinator/settable.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -12,6 +14,7 @@ extension SettableParserExtension on Parser { /// For example, the parser `letter().settable()` behaves exactly the same /// as `letter()`, but it can be replaced with another parser using /// [SettableParser.set]. + @useResult SettableParser settable() => SettableParser(this); } diff --git a/lib/src/parser/combinator/skip.dart b/lib/src/parser/combinator/skip.dart index 712e40f2..fa156bba 100644 --- a/lib/src/parser/combinator/skip.dart +++ b/lib/src/parser/combinator/skip.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../core/parser.dart'; import 'sequence.dart'; @@ -8,6 +10,7 @@ extension SkipParserExtension on Parser { /// /// For example, the parser `digit().skip(char('['), char(']'))` /// returns `'3'` for the input `'[3]'`. + @useResult Parser skip({Parser? before, Parser? after}) => before == null ? after == null ? this diff --git a/lib/src/parser/misc/eof.dart b/lib/src/parser/misc/eof.dart index 6ca84c33..523e9de0 100644 --- a/lib/src/parser/misc/eof.dart +++ b/lib/src/parser/misc/eof.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -10,11 +12,13 @@ extension EndOfInputParserExtension on Parser { /// For example, the parser `letter().end()` succeeds on the input `'a'` /// and fails on `'ab'`. In contrast the parser `letter()` alone would /// succeed on both inputs, but not consume everything for the second input. + @useResult Parser end([String message = 'end of input expected']) => seq2(this, endOfInput(message)).map2((value, _) => value); } /// Returns a parser that succeeds at the end of input. +@useResult Parser endOfInput([String message = 'end of input expected']) => EndOfInputParser(message); diff --git a/lib/src/parser/misc/epsilon.dart b/lib/src/parser/misc/epsilon.dart index 80781298..db4e5964 100644 --- a/lib/src/parser/misc/epsilon.dart +++ b/lib/src/parser/misc/epsilon.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -6,9 +8,11 @@ import '../../core/parser.dart'; /// /// For example, `char('a').or(epsilon())` is equivalent to /// `char('a').optional()`. +@useResult Parser epsilon() => epsilonWith(null); /// Returns a parser that consumes nothing and succeeds with [result]. +@useResult Parser epsilonWith(R result) => EpsilonParser(result); /// A parser that consumes nothing and succeeds. diff --git a/lib/src/parser/misc/failure.dart b/lib/src/parser/misc/failure.dart index 3cdd0a23..3c733972 100644 --- a/lib/src/parser/misc/failure.dart +++ b/lib/src/parser/misc/failure.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -5,6 +7,7 @@ import '../../core/parser.dart'; /// Returns a parser that consumes nothing and fails. /// /// For example, `failure()` always fails, no matter what input it is given. +@useResult Parser failure([String message = 'unable to parse']) => FailureParser(message); diff --git a/lib/src/parser/misc/label.dart b/lib/src/parser/misc/label.dart index cb9edce7..ecd4f3f1 100644 --- a/lib/src/parser/misc/label.dart +++ b/lib/src/parser/misc/label.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -7,6 +9,7 @@ import '../combinator/delegate.dart'; extension LabelParserExtension on Parser { /// Returns a parser that simply defers to its delegate, but that /// has a [label] for debugging purposes. + @useResult LabeledParser labeled(String label) => LabelParser(this, label); } diff --git a/lib/src/parser/misc/newline.dart b/lib/src/parser/misc/newline.dart index c2b796e6..f5dd9228 100644 --- a/lib/src/parser/misc/newline.dart +++ b/lib/src/parser/misc/newline.dart @@ -1,8 +1,11 @@ +import 'package:meta/meta.dart'; + import '../../../parser.dart'; import '../../context/context.dart'; import '../../context/result.dart'; /// Returns a parser that detects newlines platform independently. +@useResult Parser newline([String message = 'newline expected']) => NewlineParser(message); diff --git a/lib/src/parser/misc/position.dart b/lib/src/parser/misc/position.dart index 571025c7..7bfa135e 100644 --- a/lib/src/parser/misc/position.dart +++ b/lib/src/parser/misc/position.dart @@ -1,8 +1,11 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; /// Returns a parser that reports the current input position. +@useResult Parser position() => PositionParser(); /// A parser that reports the current input position. diff --git a/lib/src/parser/predicate/any.dart b/lib/src/parser/predicate/any.dart index fa2176cf..f670a6c5 100644 --- a/lib/src/parser/predicate/any.dart +++ b/lib/src/parser/predicate/any.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -6,6 +8,7 @@ import '../../core/parser.dart'; /// /// For example, `any()` succeeds and consumes any given letter. It only /// fails for an empty input. +@useResult Parser any([String message = 'input expected']) => AnyParser(message); /// A parser that accepts any input element. diff --git a/lib/src/parser/predicate/predicate.dart b/lib/src/parser/predicate/predicate.dart index 43cdaf7a..2070a571 100644 --- a/lib/src/parser/predicate/predicate.dart +++ b/lib/src/parser/predicate/predicate.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -5,6 +7,7 @@ import '../../shared/types.dart'; /// Returns a parser that reads input of the specified [length], accepts /// it if the [predicate] matches, or fails with the given [message]. +@useResult Parser predicate( int length, Predicate predicate, String message) => PredicateParser(length, predicate, message); diff --git a/lib/src/parser/predicate/string.dart b/lib/src/parser/predicate/string.dart index 092205fb..85e4ea49 100644 --- a/lib/src/parser/predicate/string.dart +++ b/lib/src/parser/predicate/string.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../core/parser.dart'; import '../character/char.dart'; import '../character/pattern.dart'; @@ -6,6 +8,7 @@ import 'predicate.dart'; extension PredicateStringExtension on String { /// Converts this string to a corresponding parser. + @useResult Parser toParser({ bool isPattern = false, bool caseInsensitive = false, @@ -35,6 +38,7 @@ extension PredicateStringExtension on String { /// /// For example, `string('foo')` `succeeds and consumes the input string /// `'foo'`. Fails for any other input.` +@useResult Parser string(String element, [String? message]) => predicate( element.length, (each) => element == each, @@ -44,6 +48,7 @@ Parser string(String element, [String? message]) => predicate( /// /// For example, `stringIgnoreCase('foo')` succeeds and consumes the input /// string `'Foo'` or `'FOO'`. Fails for any other input. +@useResult Parser stringIgnoreCase(String element, [String? message]) { final lowerElement = element.toLowerCase(); return predicate(element.length, (each) => lowerElement == each.toLowerCase(), diff --git a/lib/src/parser/repeater/greedy.dart b/lib/src/parser/repeater/greedy.dart index 29009b4c..7d8716c5 100644 --- a/lib/src/parser/repeater/greedy.dart +++ b/lib/src/parser/repeater/greedy.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -16,6 +18,7 @@ extension GreedyRepeatingParserExtension on Parser { /// /// See [starLazy] for the lazy, more efficient, and generally preferred /// variation of this combinator. + @useResult Parser> starGreedy(Parser limit) => repeatGreedy(limit, 0, unbounded); @@ -28,6 +31,7 @@ extension GreedyRepeatingParserExtension on Parser { /// /// See [plusLazy] for the lazy, more efficient, and generally preferred /// variation of this combinator. + @useResult Parser> plusGreedy(Parser limit) => repeatGreedy(limit, 1, unbounded); @@ -37,6 +41,7 @@ extension GreedyRepeatingParserExtension on Parser { /// /// This is the more generic variation of the [starGreedy] and [plusGreedy] /// combinators. + @useResult Parser> repeatGreedy(Parser limit, int min, int max) => GreedyRepeatingParser(this, limit, min, max); } diff --git a/lib/src/parser/repeater/lazy.dart b/lib/src/parser/repeater/lazy.dart index 3b677445..c274e73b 100644 --- a/lib/src/parser/repeater/lazy.dart +++ b/lib/src/parser/repeater/lazy.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -16,6 +18,7 @@ extension LazyRepeatingParserExtension on Parser { /// /// See [starGreedy] for the greedy and less efficient variation of /// this combinator. + @useResult Parser> starLazy(Parser limit) => repeatLazy(limit, 0, unbounded); @@ -28,6 +31,7 @@ extension LazyRepeatingParserExtension on Parser { /// /// See [plusGreedy] for the greedy and less efficient variation of /// this combinator. + @useResult Parser> plusLazy(Parser limit) => repeatLazy(limit, 1, unbounded); @@ -37,6 +41,7 @@ extension LazyRepeatingParserExtension on Parser { /// /// This is the more generic variation of the [starLazy] and [plusLazy] /// combinators. + @useResult Parser> repeatLazy(Parser limit, int min, int max) => LazyRepeatingParser(this, limit, min, max); } diff --git a/lib/src/parser/repeater/possessive.dart b/lib/src/parser/repeater/possessive.dart index 8d0320e7..e792853a 100644 --- a/lib/src/parser/repeater/possessive.dart +++ b/lib/src/parser/repeater/possessive.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -14,6 +16,7 @@ extension PossessiveRepeatingParserExtension on Parser { /// For example, the parser `letter().star()` accepts the empty string or /// any sequence of letters and returns a possibly empty list of the parsed /// letters. + @useResult Parser> star() => repeat(0, unbounded); /// Returns a parser that accepts the receiver one or more times. The @@ -24,6 +27,7 @@ extension PossessiveRepeatingParserExtension on Parser { /// /// For example, the parser `letter().plus()` accepts any sequence of /// letters and returns a list of the parsed letters. + @useResult Parser> plus() => repeat(1, unbounded); /// Returns a parser that accepts the receiver exactly [count] times. The @@ -31,6 +35,7 @@ extension PossessiveRepeatingParserExtension on Parser { /// /// For example, the parser `letter().times(2)` accepts two letters and /// returns a list of the two parsed letters. + @useResult Parser> times(int count) => repeat(count, count); /// Returns a parser that accepts the receiver between [min] and [max] times. @@ -41,6 +46,7 @@ extension PossessiveRepeatingParserExtension on Parser { /// /// For example, the parser `letter().repeat(2, 4)` accepts a sequence of /// two, three, or four letters and returns the accepted letters as a list. + @useResult Parser> repeat(int min, [int? max]) => PossessiveRepeatingParser(this, min, max ?? min); } diff --git a/lib/src/parser/repeater/separated.dart b/lib/src/parser/repeater/separated.dart index aad52f08..433e0562 100644 --- a/lib/src/parser/repeater/separated.dart +++ b/lib/src/parser/repeater/separated.dart @@ -1,5 +1,7 @@ import 'dart:math' as math; +import 'package:meta/meta.dart'; + import '../../context/context.dart'; import '../../context/result.dart'; import '../../core/parser.dart'; @@ -17,6 +19,7 @@ extension SeparatedRepeatingParserExtension on Parser { /// parser that consumes input like `'1,2;3'` and that returns a /// [SeparatedList] with elements `['1', '2', '3']` as well as the separators /// [`,`, `;`]. + @useResult Parser> starSeparated(Parser separator) => repeatSeparated(separator, 0, unbounded); @@ -24,6 +27,7 @@ extension SeparatedRepeatingParserExtension on Parser { /// by the [separator] parser. The resulting parser returns a [SeparatedList] /// containing collections of both the elements of type [R] as well as the /// separators of type [S]. + @useResult Parser> plusSeparated(Parser separator) => repeatSeparated(separator, 1, unbounded); @@ -31,6 +35,7 @@ extension SeparatedRepeatingParserExtension on Parser { /// by the [separator] parser. The resulting parser returns a [SeparatedList] /// containing collections of both the elements of type [R] as well as the /// separators of type [S]. + @useResult Parser> timesSeparated( Parser separator, int count) => repeatSeparated(separator, count, count); @@ -39,6 +44,7 @@ extension SeparatedRepeatingParserExtension on Parser { /// separated by the [separator] parser. The resulting parser returns a /// [SeparatedList] containing collections of both the elements of type [R] as /// well as the separators of type [S]. + @useResult Parser> repeatSeparated( Parser separator, int min, int max) => SeparatedRepeatingParser(this, separator, min, max); diff --git a/lib/src/parser/repeater/separated_by.dart b/lib/src/parser/repeater/separated_by.dart index f83c4922..52b687d4 100644 --- a/lib/src/parser/repeater/separated_by.dart +++ b/lib/src/parser/repeater/separated_by.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + import '../../core/parser.dart'; import '../combinator/optional.dart'; import '../combinator/sequence.dart'; @@ -22,6 +24,7 @@ extension SeparatedByParserExtension on Parser { /// separators: `['1', '-', '2', '-', '3']`. @Deprecated('Use `plusSeparated` for a better optimized and strongly typed ' 'implementation that provides the elements and separators separately') + @useResult Parser> separatedBy( Parser separator, { bool includeSeparators = true, diff --git a/lib/src/parser/utils/resolvable.dart b/lib/src/parser/utils/resolvable.dart index b9df579c..93ded4f5 100644 --- a/lib/src/parser/utils/resolvable.dart +++ b/lib/src/parser/utils/resolvable.dart @@ -1,7 +1,10 @@ +import 'package:meta/meta.dart'; + import '../../core/parser.dart'; /// Interface of a parser that can be resolved to another one. abstract class ResolvableParser implements Parser { /// Resolves this parser with another one of the same type. + @useResult Parser resolve(); } diff --git a/pubspec.yaml b/pubspec.yaml index 7117fbac..10db1c58 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: petitparser -version: 5.1.0 +version: 5.2.0 homepage: https://petitparser.github.io repository: https://github.com/petitparser/dart-petitparser