Updated to Null Safety
This commit is contained in:
parent
cb56b31d10
commit
01171d8954
24 changed files with 101 additions and 94 deletions
|
@ -8,7 +8,7 @@ class _Advance<T> extends Parser<T> {
|
|||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth())!.change(parser: this);
|
||||
var result = parser._parse(args.increaseDepth()).change(parser: this);
|
||||
if (result.successful) args.scanner.position += amount;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ part of lex.src.combinator;
|
|||
/// generate any error at all.
|
||||
Parser<T> any<T>(Iterable<Parser<T>> parsers,
|
||||
{bool backtrack: true, errorMessage, SyntaxErrorSeverity? severity}) {
|
||||
return new _Any(parsers, backtrack != false, errorMessage,
|
||||
return _Any(parsers, backtrack != false, errorMessage,
|
||||
severity ?? SyntaxErrorSeverity.error);
|
||||
}
|
||||
|
||||
|
@ -26,14 +26,14 @@ class _Any<T> extends Parser<T> {
|
|||
.where((p) => !args.trampoline.isActive(p, args.scanner.position));
|
||||
|
||||
if (inactive.isEmpty) {
|
||||
return new ParseResult(args.trampoline, args.scanner, this, false, []);
|
||||
return ParseResult(args.trampoline, args.scanner, this, false, []);
|
||||
}
|
||||
|
||||
var errors = <SyntaxError>[];
|
||||
int replay = args.scanner.position;
|
||||
|
||||
for (var parser in inactive) {
|
||||
var result = parser._parse(args.increaseDepth())!;
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
|
||||
if (result.successful)
|
||||
return result;
|
||||
|
@ -45,7 +45,7 @@ class _Any<T> extends Parser<T> {
|
|||
|
||||
if (errorMessage != false) {
|
||||
errors.add(
|
||||
new SyntaxError(
|
||||
SyntaxError(
|
||||
severity,
|
||||
errorMessage?.toString() ??
|
||||
'No match found for ${parsers.length} alternative(s)',
|
||||
|
@ -54,13 +54,13 @@ class _Any<T> extends Parser<T> {
|
|||
);
|
||||
}
|
||||
|
||||
return new ParseResult(args.trampoline, args.scanner, this, false, errors);
|
||||
return ParseResult(args.trampoline, args.scanner, this, false, errors);
|
||||
}
|
||||
|
||||
@override
|
||||
ParseResult<T>? __parse(ParseArgs args) {
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
// Never called
|
||||
return null;
|
||||
throw ArgumentError("[Combinator] Invalid method call");
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -7,7 +7,7 @@ class _Cast<T, U extends T> extends Parser<U> {
|
|||
|
||||
@override
|
||||
ParseResult<U> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth())!;
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
return new ParseResult<U>(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
|
@ -38,7 +38,7 @@ class _CastDynamic<T> extends Parser<dynamic> {
|
|||
|
||||
@override
|
||||
ParseResult<dynamic> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth())!;
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
return new ParseResult<dynamic>(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
|
|
|
@ -18,7 +18,7 @@ class _Alt<T> extends Parser<T> {
|
|||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth())!;
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
return result.successful
|
||||
? result
|
||||
: result.addErrors([
|
||||
|
@ -48,7 +48,7 @@ class _Chain<T> extends ListParser<T> {
|
|||
bool successful = true;
|
||||
|
||||
for (var parser in parsers) {
|
||||
var result = parser._parse(args.increaseDepth())!;
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
|
||||
if (!result.successful) {
|
||||
if (parser is _Alt) errors.addAll(result.errors);
|
||||
|
|
|
@ -11,7 +11,7 @@ class _Check<T> extends Parser<T> {
|
|||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
var matchState = {};
|
||||
var result = parser._parse(args.increaseDepth())!.change(parser: this);
|
||||
var result = parser._parse(args.increaseDepth()).change(parser: this);
|
||||
if (!result.successful)
|
||||
return result;
|
||||
else if (!matcher.matches(result.value, matchState)) {
|
||||
|
|
|
@ -67,9 +67,9 @@ class ParseArgs {
|
|||
|
||||
/// A parser combinator, which can parse very complicated grammars in a manageable manner.
|
||||
abstract class Parser<T> {
|
||||
ParseResult<T>? __parse(ParseArgs args);
|
||||
ParseResult<T> __parse(ParseArgs args);
|
||||
|
||||
ParseResult<T>? _parse(ParseArgs args) {
|
||||
ParseResult<T> _parse(ParseArgs args) {
|
||||
var pos = args.scanner.position;
|
||||
|
||||
if (args.trampoline.hasMemoized(this, pos))
|
||||
|
@ -86,7 +86,7 @@ abstract class Parser<T> {
|
|||
}
|
||||
|
||||
/// Parses text from a [SpanScanner].
|
||||
ParseResult<T>? parse(SpanScanner scanner, [int depth = 1]) {
|
||||
ParseResult<T> parse(SpanScanner scanner, [int depth = 1]) {
|
||||
var args = ParseArgs(Trampoline(), scanner, depth);
|
||||
return _parse(args);
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ abstract class Parser<T> {
|
|||
|
||||
// TODO: Type issue
|
||||
/// Runs the given function, which changes the returned [ParseResult] into one relating to a [U] object.
|
||||
Parser<U> change<U>(ParseResult<U> Function(ParseResult<T>?) f) {
|
||||
Parser<U> change<U>(ParseResult<U> Function(ParseResult<T>) f) {
|
||||
return _Change<T, U>(this, f);
|
||||
}
|
||||
|
||||
|
@ -140,8 +140,10 @@ abstract class Parser<T> {
|
|||
/// Ensures this pattern is not matched.
|
||||
///
|
||||
/// You can provide an [errorMessage].
|
||||
Parser<T> negate({String? errorMessage, SyntaxErrorSeverity? severity}) =>
|
||||
_Negate<T>(this, errorMessage, severity ?? SyntaxErrorSeverity.error);
|
||||
Parser<T> negate(
|
||||
{String errorMessage = 'Negate error',
|
||||
SyntaxErrorSeverity severity = SyntaxErrorSeverity.error}) =>
|
||||
_Negate<T>(this, errorMessage, severity);
|
||||
|
||||
/// Caches the results of parse attempts at various locations within the source text.
|
||||
///
|
||||
|
@ -153,7 +155,7 @@ abstract class Parser<T> {
|
|||
/// Consumes `this` and another parser, but only considers the result of `this` parser.
|
||||
Parser<T> and(Parser other) => then(other).change<T>((r) {
|
||||
return ParseResult<T>(
|
||||
r!.trampoline,
|
||||
r.trampoline,
|
||||
r.scanner,
|
||||
this,
|
||||
r.successful,
|
||||
|
@ -176,7 +178,7 @@ abstract class Parser<T> {
|
|||
/// The generated parser only runs once; repeated uses always exit eagerly.
|
||||
Parser<T> safe(
|
||||
{bool backtrack: true,
|
||||
String? errorMessage,
|
||||
String errorMessage = "error",
|
||||
SyntaxErrorSeverity? severity}) =>
|
||||
_Safe<T>(
|
||||
this, backtrack, errorMessage, severity ?? SyntaxErrorSeverity.error);
|
||||
|
@ -240,7 +242,7 @@ abstract class Parser<T> {
|
|||
Parser<T> space() => trail(RegExp(r'[ \n\r\t]+'));
|
||||
|
||||
/// Consumes 0 or more instance(s) of this parser.
|
||||
ListParser<T?> star({bool backtrack: true}) =>
|
||||
ListParser<T> star({bool backtrack: true}) =>
|
||||
times(1, exact: false, backtrack: backtrack).opt();
|
||||
|
||||
/// Shortcut for [chain]-ing two parsers together.
|
||||
|
@ -259,10 +261,10 @@ abstract class Parser<T> {
|
|||
/// an infinite amount of occurrences after the specified [count].
|
||||
///
|
||||
/// You can provide custom error messages for when there are [tooFew] or [tooMany] occurrences.
|
||||
ListParser<T?> times(int count,
|
||||
ListParser<T> times(int count,
|
||||
{bool exact: true,
|
||||
String? tooFew,
|
||||
String? tooMany,
|
||||
String tooFew = 'Too few',
|
||||
String tooMany = 'Too many',
|
||||
bool backtrack: true,
|
||||
SyntaxErrorSeverity? severity}) {
|
||||
return _Repeat<T>(this, count, exact, tooFew, tooMany, backtrack,
|
||||
|
|
|
@ -8,7 +8,7 @@ class _Compare<T> extends ListParser<T> {
|
|||
|
||||
@override
|
||||
ParseResult<List<T>> __parse(ParseArgs args) {
|
||||
ParseResult<List<T>> result = parser._parse(args.increaseDepth())!;
|
||||
ParseResult<List<T>> result = parser._parse(args.increaseDepth());
|
||||
if (!result.successful) return result;
|
||||
|
||||
result = result.change(
|
||||
|
|
|
@ -8,7 +8,7 @@ class _FoldErrors<T> extends Parser<T> {
|
|||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth())!.change(parser: this);
|
||||
var result = parser._parse(args.increaseDepth()).change(parser: this);
|
||||
var errors = result.errors.fold<List<SyntaxError>>([], (out, e) {
|
||||
if (!out.any((b) => equal(e, b))) out.add(e);
|
||||
return out;
|
||||
|
|
|
@ -8,7 +8,7 @@ class _Index<T> extends Parser<T> {
|
|||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<List<T>> result = parser._parse(args.increaseDepth())!;
|
||||
ParseResult<List<T>> result = parser._parse(args.increaseDepth());
|
||||
Object? value;
|
||||
|
||||
if (result.successful)
|
||||
|
|
|
@ -31,7 +31,7 @@ class _Longest<T> extends Parser<T> {
|
|||
var results = <ParseResult<T>>[];
|
||||
|
||||
for (var parser in inactive) {
|
||||
var result = parser._parse(args.increaseDepth())!;
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
|
||||
if (result.successful && result.span != null)
|
||||
results.add(result);
|
||||
|
@ -66,7 +66,7 @@ class _Longest<T> extends Parser<T> {
|
|||
var results = <ParseResult<T>>[];
|
||||
|
||||
for (var parser in parsers) {
|
||||
var result = parser._parse(args.increaseDepth())!;
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
|
||||
if (result.successful)
|
||||
results.add(result);
|
||||
|
|
|
@ -8,8 +8,8 @@ class _Map<T, U> extends Parser<U> {
|
|||
|
||||
@override
|
||||
ParseResult<U> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth())!;
|
||||
return new ParseResult<U>(
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
return ParseResult<U>(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
this,
|
||||
|
@ -34,7 +34,7 @@ class _Map<T, U> extends Parser<U> {
|
|||
|
||||
class _Change<T, U> extends Parser<U> {
|
||||
final Parser<T> parser;
|
||||
final ParseResult<U> Function(ParseResult<T>?) f;
|
||||
final ParseResult<U> Function(ParseResult<T>) f;
|
||||
|
||||
_Change(this.parser, this.f);
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ class _MaxDepth<T> extends Parser<T> {
|
|||
_MaxDepth(this.parser, this.cap);
|
||||
|
||||
@override
|
||||
ParseResult<T>? __parse(ParseArgs args) {
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
if (args.depth > cap) {
|
||||
return new ParseResult<T>(args.trampoline, args.scanner, this, false, []);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ class _Negate<T> extends Parser<T> {
|
|||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth())!.change(parser: this);
|
||||
var result = parser._parse(args.increaseDepth()).change(parser: this);
|
||||
|
||||
if (!result.successful) {
|
||||
return new ParseResult<T>(
|
||||
|
|
|
@ -9,7 +9,7 @@ class _Opt<T> extends Parser<T> {
|
|||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
var replay = args.scanner.position;
|
||||
var result = parser._parse(args.increaseDepth())!;
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
|
||||
if (!result.successful) args.scanner.position = replay;
|
||||
|
||||
|
@ -37,7 +37,7 @@ class _ListOpt<T> extends ListParser<T> {
|
|||
@override
|
||||
ParseResult<List<T>> __parse(ParseArgs args) {
|
||||
var replay = args.scanner.position;
|
||||
ParseResult<List<T>> result = parser._parse(args.increaseDepth())!;
|
||||
ParseResult<List<T>> result = parser._parse(args.increaseDepth());
|
||||
|
||||
if (!result.successful) args.scanner.position = replay;
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ class _Reduce<T> extends Parser<T> {
|
|||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<List<T>> result = parser._parse(args.increaseDepth())!;
|
||||
ParseResult<List<T>> result = parser._parse(args.increaseDepth());
|
||||
|
||||
if (!result.successful)
|
||||
return new ParseResult<T>(
|
||||
|
|
|
@ -16,22 +16,26 @@ class Reference<T> extends Parser<T> {
|
|||
}
|
||||
|
||||
@override
|
||||
ParseResult<T>? __parse(ParseArgs args) {
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
if (_parser == null)
|
||||
throw new StateError('There is no parser assigned to this reference.');
|
||||
return _parser!._parse(args);
|
||||
}
|
||||
|
||||
@override
|
||||
ParseResult<T>? _parse(ParseArgs args) {
|
||||
ParseResult<T> _parse(ParseArgs args) {
|
||||
if (_parser == null)
|
||||
throw new StateError('There is no parser assigned to this reference.');
|
||||
return _parser!._parse(args);
|
||||
}
|
||||
|
||||
@override
|
||||
void stringify(CodeBuffer buffer) {
|
||||
if (_parser == null)
|
||||
if (_parser == null) {
|
||||
buffer.writeln('(undefined reference <$T>)');
|
||||
else if (!printed) _parser!.stringify(buffer);
|
||||
} else if (!printed) {
|
||||
_parser!.stringify(buffer);
|
||||
}
|
||||
printed = true;
|
||||
buffer.writeln('(previously printed reference)');
|
||||
}
|
||||
|
|
|
@ -1,63 +1,67 @@
|
|||
part of lex.src.combinator;
|
||||
|
||||
class _Repeat<T> extends ListParser<T?> {
|
||||
class _Repeat<T> extends ListParser<T> {
|
||||
final Parser<T> parser;
|
||||
final int count;
|
||||
final bool exact, backtrack;
|
||||
final String? tooFew, tooMany;
|
||||
final String tooFew;
|
||||
final String tooMany;
|
||||
final SyntaxErrorSeverity severity;
|
||||
|
||||
_Repeat(this.parser, this.count, this.exact, this.tooFew, this.tooMany,
|
||||
this.backtrack, this.severity);
|
||||
|
||||
@override
|
||||
ParseResult<List<T?>> __parse(ParseArgs args) {
|
||||
ParseResult<List<T>> __parse(ParseArgs args) {
|
||||
var errors = <SyntaxError>[];
|
||||
var results = <T?>[];
|
||||
var spans = <FileSpan?>[];
|
||||
var results = <T>[];
|
||||
var spans = <FileSpan>[];
|
||||
int success = 0, replay = args.scanner.position;
|
||||
ParseResult<T>? result;
|
||||
|
||||
do {
|
||||
result = parser._parse(args.increaseDepth());
|
||||
if (result!.successful) {
|
||||
if (result.successful) {
|
||||
success++;
|
||||
results.add(result.value);
|
||||
if (result.value != null) {
|
||||
results.add(result.value!);
|
||||
}
|
||||
replay = args.scanner.position;
|
||||
} else if (backtrack) args.scanner.position = replay;
|
||||
|
||||
if (result.span != null) spans.add(result.span);
|
||||
if (result.span != null) {
|
||||
spans.add(result.span!);
|
||||
}
|
||||
} while (result.successful);
|
||||
|
||||
if (success < count) {
|
||||
errors.addAll(result.errors);
|
||||
errors.add(
|
||||
new SyntaxError(
|
||||
SyntaxError(
|
||||
severity,
|
||||
tooFew ?? 'Expected at least $count occurence(s).',
|
||||
tooFew,
|
||||
result.span ?? args.scanner.emptySpan,
|
||||
),
|
||||
);
|
||||
|
||||
if (backtrack) args.scanner.position = replay;
|
||||
|
||||
return new ParseResult<List<T?>>(
|
||||
return ParseResult<List<T>>(
|
||||
args.trampoline, args.scanner, this, false, errors);
|
||||
} else if (success > count && exact) {
|
||||
if (backtrack) args.scanner.position = replay;
|
||||
|
||||
return new ParseResult<List<T?>>(
|
||||
args.trampoline, args.scanner, this, false, [
|
||||
new SyntaxError(
|
||||
return ParseResult<List<T>>(args.trampoline, args.scanner, this, false, [
|
||||
SyntaxError(
|
||||
severity,
|
||||
tooMany ?? 'Expected no more than $count occurence(s).',
|
||||
tooMany,
|
||||
result.span ?? args.scanner.emptySpan,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
var span = spans.reduce((a, b) => a!.expand(b!));
|
||||
return new ParseResult<List<T?>>(
|
||||
var span = spans.reduce((a, b) => a.expand(b));
|
||||
return ParseResult<List<T>>(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
this,
|
||||
|
@ -70,7 +74,7 @@ class _Repeat<T> extends ListParser<T?> {
|
|||
|
||||
@override
|
||||
void stringify(CodeBuffer buffer) {
|
||||
var r = new StringBuffer('{$count');
|
||||
var r = StringBuffer('{$count');
|
||||
if (!exact) r.write(',');
|
||||
r.write('}');
|
||||
buffer
|
||||
|
|
|
@ -3,14 +3,14 @@ part of lex.src.combinator;
|
|||
class _Safe<T> extends Parser<T> {
|
||||
final Parser<T> parser;
|
||||
final bool backtrack;
|
||||
final String? errorMessage;
|
||||
final String errorMessage;
|
||||
final SyntaxErrorSeverity severity;
|
||||
bool _triggered = false;
|
||||
|
||||
_Safe(this.parser, this.backtrack, this.errorMessage, this.severity);
|
||||
|
||||
@override
|
||||
ParseResult<T>? __parse(ParseArgs args) {
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
var replay = args.scanner.position;
|
||||
|
||||
try {
|
||||
|
@ -21,8 +21,6 @@ class _Safe<T> extends Parser<T> {
|
|||
if (backtrack) args.scanner.position = replay;
|
||||
var errors = <SyntaxError>[];
|
||||
|
||||
if (errorMessage != null) {
|
||||
// TODO: Custom severity for all errors?
|
||||
errors.add(
|
||||
new SyntaxError(
|
||||
severity,
|
||||
|
@ -30,7 +28,6 @@ class _Safe<T> extends Parser<T> {
|
|||
args.scanner.lastSpan ?? args.scanner.emptySpan,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return new ParseResult<T>(
|
||||
args.trampoline, args.scanner, this, false, errors);
|
||||
|
|
|
@ -7,7 +7,7 @@ class _ToList<T> extends ListParser<T?> {
|
|||
|
||||
@override
|
||||
ParseResult<List<T?>> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth())!;
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
|
||||
if (result.value is List) {
|
||||
return (result as ParseResult<List<T?>>).change(parser: this);
|
||||
|
|
|
@ -8,7 +8,7 @@ class _Value<T> extends Parser<T> {
|
|||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth())!.change(parser: this);
|
||||
var result = parser._parse(args.increaseDepth()).change(parser: this);
|
||||
return result.successful ? result.change(value: f(result)) : result;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,11 +12,11 @@ main() {
|
|||
|
||||
test('sort', () {
|
||||
var parser = numbers.sort((a, b) => a!.compareTo(b!));
|
||||
expect(parser.parse(scan('21,2,3,34,20'))!.value, [2, 3, 20, 21, 34]);
|
||||
expect(parser.parse(scan('21,2,3,34,20')).value, [2, 3, 20, 21, 34]);
|
||||
});
|
||||
test('reduce', () {
|
||||
var parser = numbers.reduce((a, b) => a! + b!);
|
||||
expect(parser.parse(scan('21,2,3,34,20'))!.value, 80);
|
||||
expect(parser.parse(scan('not numbers'))!.value, isNull);
|
||||
expect(parser.parse(scan('21,2,3,34,20')).value, 80);
|
||||
expect(parser.parse(scan('not numbers')).value, isNull);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,13 +4,13 @@ import 'common.dart';
|
|||
|
||||
main() {
|
||||
test('match string', () {
|
||||
expect(match('hello').parse(scan('hello world'))!.successful, isTrue);
|
||||
expect(match('hello').parse(scan('hello world')).successful, isTrue);
|
||||
});
|
||||
test('match start only', () {
|
||||
expect(match('hello').parse(scan('goodbye hello'))!.successful, isFalse);
|
||||
expect(match('hello').parse(scan('goodbye hello')).successful, isFalse);
|
||||
});
|
||||
|
||||
test('fail if no match', () {
|
||||
expect(match('hello').parse(scan('world'))!.successful, isFalse);
|
||||
expect(match('hello').parse(scan('world')).successful, isFalse);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -14,29 +14,29 @@ main() {
|
|||
});
|
||||
|
||||
test('change', () {
|
||||
var parser = match('hello').change((r) => r!.change(value: 23));
|
||||
expect(parser.parse(scan('helloworld'))!.value, 23);
|
||||
var parser = match('hello').change((r) => r.change(value: 23));
|
||||
expect(parser.parse(scan('helloworld')).value, 23);
|
||||
});
|
||||
|
||||
test('check', () {
|
||||
var parser = match<int>(new RegExp(r'[A-Za-z]+'))
|
||||
.value((r) => r.span!.length)
|
||||
.check(greaterThan(3));
|
||||
expect(parser.parse(scan('helloworld'))!.successful, isTrue);
|
||||
expect(parser.parse(scan('yo'))!.successful, isFalse);
|
||||
expect(parser.parse(scan('helloworld')).successful, isTrue);
|
||||
expect(parser.parse(scan('yo')).successful, isFalse);
|
||||
});
|
||||
|
||||
test('map', () {
|
||||
var parser =
|
||||
match(new RegExp(r'[A-Za-z]+')).map<int>((r) => r.span!.length);
|
||||
expect(parser.parse(scan('hello'))!.value, 5);
|
||||
expect(parser.parse(scan('hello')).value, 5);
|
||||
});
|
||||
|
||||
test('negate', () {
|
||||
var parser = match('hello').negate(errorMessage: 'world');
|
||||
expect(parser.parse(scan('goodbye world'))!.successful, isTrue);
|
||||
expect(parser.parse(scan('hello world'))!.successful, isFalse);
|
||||
expect(parser.parse(scan('hello world'))!.errors.first.message, 'world');
|
||||
expect(parser.parse(scan('goodbye world')).successful, isTrue);
|
||||
expect(parser.parse(scan('hello world')).successful, isFalse);
|
||||
expect(parser.parse(scan('hello world')).errors.first.message, 'world');
|
||||
});
|
||||
|
||||
group('opt', () {
|
||||
|
@ -44,13 +44,13 @@ main() {
|
|||
var list = match('hel').then(match('lo')).opt();
|
||||
|
||||
test('succeeds if present', () {
|
||||
expect(single.parse(scan('hello'))!.successful, isTrue);
|
||||
expect(list.parse(scan('hello'))!.successful, isTrue);
|
||||
expect(single.parse(scan('hello')).successful, isTrue);
|
||||
expect(list.parse(scan('hello')).successful, isTrue);
|
||||
});
|
||||
|
||||
test('succeeds if not present', () {
|
||||
expect(single.parse(scan('goodbye'))!.successful, isTrue);
|
||||
expect(list.parse(scan('goodbye'))!.successful, isTrue);
|
||||
expect(single.parse(scan('goodbye')).successful, isTrue);
|
||||
expect(list.parse(scan('goodbye')).successful, isTrue);
|
||||
});
|
||||
|
||||
test('backtracks if not present', () {
|
||||
|
|
|
@ -6,10 +6,10 @@ main() {
|
|||
var parser = match('hello').value((r) => 'world');
|
||||
|
||||
test('sets value', () {
|
||||
expect(parser.parse(scan('hello world'))!.value, 'world');
|
||||
expect(parser.parse(scan('hello world')).value, 'world');
|
||||
});
|
||||
|
||||
test('no value if no match', () {
|
||||
expect(parser.parse(scan('goodbye world'))!.value, isNull);
|
||||
expect(parser.parse(scan('goodbye world')).value, isNull);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue