blob: 8c17cd8f92d0f0ece6633eeae19c1103ecfea84f [file] [log] [blame]
import 'package:meta/meta.dart';
import '../../context/context.dart';
import '../../context/result.dart';
import '../../core/parser.dart';
import 'greedy.dart';
import 'limited.dart';
import 'possessive.dart';
import 'unbounded.dart';
extension LazyRepeatingParserExtension<R> on Parser<R> {
/// Returns a parser that parses the receiver zero or more times until it
/// reaches a [limit]. This is a lazy non-blind implementation of the [star]
/// operator. The [limit] is not consumed.
///
/// For example, the parser `char('{') & any().starLazy(char('}')) &
/// char('}')` only consumes the part `'{abc}'` of `'{abc}def}'`.
///
/// See [starGreedy] for the greedy and less efficient variation of
/// this combinator.
@useResult
Parser<List<R>> starLazy(Parser<void> limit) =>
repeatLazy(limit, 0, unbounded);
/// Returns a parser that parses the receiver one or more times until it
/// reaches a [limit]. This is a lazy non-blind implementation of the [plus]
/// operator. The [limit] is not consumed.
///
/// For example, the parser `char('{') & any().plusLazy(char('}')) &
/// char('}')` only consumes the part `'{abc}'` of `'{abc}def}'`.
///
/// See [plusGreedy] for the greedy and less efficient variation of
/// this combinator.
@useResult
Parser<List<R>> plusLazy(Parser<void> limit) =>
repeatLazy(limit, 1, unbounded);
/// Returns a parser that parses the receiver at least [min] and at most [max]
/// times until it reaches a [limit]. This is a lazy non-blind implementation
/// of the [repeat] operator. The [limit] is not consumed.
///
/// This is the more generic variation of the [starLazy] and [plusLazy]
/// combinators.
@useResult
Parser<List<R>> repeatLazy(Parser<void> limit, int min, int max) =>
LazyRepeatingParser<R>(this, limit, min, max);
}
/// A lazy repeating parser, commonly seen in regular expression
/// implementations. It limits its consumption to meet the 'limit' condition as
/// early as possible.
class LazyRepeatingParser<R> extends LimitedRepeatingParser<R> {
LazyRepeatingParser(super.parser, super.limit, super.min, super.max);
@override
Result<List<R>> parseOn(Context context) {
var current = context;
final elements = <R>[];
while (elements.length < min) {
final result = delegate.parseOn(current);
if (result.isFailure) {
return result.failure(result.message);
}
elements.add(result.value);
current = result;
}
for (;;) {
final limiter = limit.parseOn(current);
if (limiter.isSuccess) {
return current.success(elements);
} else {
if (elements.length >= max) {
return limiter.failure(limiter.message);
}
final result = delegate.parseOn(current);
if (result.isFailure) {
return limiter.failure(limiter.message);
}
elements.add(result.value);
current = result;
}
}
}
@override
int fastParseOn(String buffer, int position) {
var count = 0;
var current = position;
while (count < min) {
final result = delegate.fastParseOn(buffer, current);
if (result < 0) {
return -1;
}
current = result;
count++;
}
for (;;) {
final limiter = limit.fastParseOn(buffer, current);
if (limiter >= 0) {
return current;
} else {
if (count >= max) {
return -1;
}
final result = delegate.fastParseOn(buffer, current);
if (result < 0) {
return -1;
}
current = result;
count++;
}
}
}
@override
LazyRepeatingParser<R> copy() =>
LazyRepeatingParser<R>(delegate, limit, min, max);
}