Parser<A> class
class Parser<A> {
final _ParseFunction _run;
Parser(ParseResult<A> f(String s, Position pos)) : this._run = f;
ParseResult run(String s, [Position pos = const Position(0, 1, 1)]) =>
_run(s, pos);
A parse(String s) {
ParseResult<A> result = run(s);
if (result.isSuccess) return result.value;
else throw result.errorMessage;
}
/// Monadic bind.
Parser operator >>(Parser g(A x)) {
return new Parser((text, pos) {
ParseResult res = _run(text, pos);
if (res.isSuccess) {
final res2 = g(res.value)._run(text, res.position);
return res2.copy(
expectations: res.expectations.best(res2.expectations),
isCommitted: res.isCommitted || res2.isCommitted);
} else {
return res;
}
});
}
Parser expecting(String expected) {
return new Parser((s, pos) {
final res = _run(s, pos);
return res.copy(expectations: _singleExpectation(expected, pos));
});
}
Parser get committed {
return new Parser((s, pos) {
final res = _run(s, pos);
return res.copy(isCommitted: true);
});
}
/// Alias for [:expecting:].
Parser<A> operator %(String expected) => this.expecting(expected);
/// Applicative <*>
Parser operator *(Parser p) => this >> (f) => p >> (x) {
Function ff = f;
return success(ff(x));
};
/// Applicative *>
Parser operator >(Parser p) => this >> (_) => p;
/// Applicative <*
Parser<A> operator <(Parser p) => this >> ((x) => p > success(x));
/// Functor map
Parser map(Object f(A x)) => success(f) * this;
/// Infix syntax for map
Parser operator ^(Object f(A x)) => map(f);
/// Parser sequencing: creates a parser accumulator.
ParserAccumulator2 operator +(Parser p) => new ParserAccumulator2(this, p);
/// Alternative
Parser operator |(Parser p) {
return new Parser((s, pos) {
ParseResult<A> res = _run(s, pos);
if (res.isSuccess || res.isCommitted) {
return res;
} else {
ParseResult res2 = p._run(s, pos);
return res2.copy(
expectations: res.expectations.best(res2.expectations));
}
});
}
/**
* Parses without consuming any input.
*
* Used for defining followedBy, which is probably what you're looking for.
*/
Parser<A> get lookAhead {
return new Parser((s, pos) {
ParseResult res = _run(s, pos);
return res.isSuccess
? _success(res.value, s, pos)
: res;
});
}
/**
* Succeeds if and only if [this] succeeds and [p] succeeds on what remains to
* parse without cosuming it.
*
* string("let").followedBy(space)
*/
Parser<A> followedBy(Parser p) => this < p.lookAhead;
/**
* Fails if and only if [this] succeeds on what's ahead.
*
* Used for defining notFollowedBy, which is probably what you're looking for.
*/
Parser get notAhead {
return new Parser((s, pos) {
ParseResult res = _run(s, pos);
return res.isSuccess
? _failure(s, pos)
: _success(null, s, pos);
});
}
/**
* Succeeds if and only if [this] succeeds and [p] fails on what remains to
* parse.
*
* string("let").notFollowedBy(alphanum)
*/
Parser<A> notFollowedBy(Parser p) => this < p.notAhead;
/**
* Parses [this] 0 or more times until [end] succeeds.
*
* Returns the list of values returned by [this]. It is useful for parsing
* comments.
*
* string('/*') > anyChar.manyUntil(string('*/'))
*
* The input parsed by [end] is consumed. Use [:end.lookAhead:] if you don't
* want this.
*/
Parser<List<A>> manyUntil(Parser end) {
// Imperative version to avoid stack overflows.
return new Parser((s, pos) {
List res = [];
Position index = pos;
var exps = _emptyExpectation(pos);
bool committed = false;
while(true) {
final endRes = end._run(s, index);
exps = exps.best(endRes.expectations);
if (endRes.isSuccess) {
return endRes.copy(value: res, expectations: exps,
isCommitted: committed);
} else if (!endRes.isCommitted) {
final xRes = this._run(s, index);
exps = exps.best(xRes.expectations);
committed = committed || xRes.isCommitted;
if (xRes.isSuccess) {
res.add(xRes.value);
index = xRes.position;
} else {
return xRes.copy(expectations: exps, isCommitted: committed);
}
} else {
return endRes.copy(expectations: exps, isCommitted: committed);
}
}
});
}
/**
* Parses [this] 0 or more times until [end] succeeds and discards the result.
*
* Equivalent to [:this.manyUntil(end) > success(null):] but faster. The input
* parsed by [end] is consumed. Use [:end.lookAhead:] if you don't want this.
*/
Parser skipManyUntil(Parser end) {
// Imperative version to avoid stack overflows.
return new Parser((s, pos) {
Position index = pos;
var exps = _emptyExpectation(pos);
var commit = false;
while(true) {
final endRes = end._run(s, index);
exps = exps.best(endRes.expectations);
commit = commit || endRes.isCommitted;
if (endRes.isSuccess) {
return endRes.copy(value: null, expectations: exps,
isCommitted: commit);
} else if (!endRes.isCommitted) {
final xRes = this._run(s, index);
exps = exps.best(xRes.expectations);
commit = commit || xRes.isCommitted;
if (xRes.isSuccess) {
index = xRes.position;
} else {
return xRes.copy(expectations: exps, isCommitted: commit);
}
} else {
return endRes.copy(expectations: exps);
}
}
});
}
// Derived combinators, defined here for infix notation
Parser<A> orElse(A value) => this | success(value);
Parser<Option<A>> get maybe => this.map(_some).orElse(_none);
// Imperative version to avoid stack overflows.
Parser<List<A>> _many(List<A> acc()) {
return new Parser((s, pos) {
final res = acc();
var exps = _emptyExpectation(pos);
Position index = pos;
bool committed = false;
while(true) {
ParseResult<A> o = this._run(s, index);
exps = exps.best(o.expectations);
committed = committed || o.isCommitted;
if (o.isSuccess) {
res.add(o.value);
index = o.position;
} else if (o.isCommitted) {
return o.copy(expectations: exps);
} else {
return _success(res, s, index, exps, committed);
}
}
});
}
Parser<List<A>> get many => _many(() => []);
Parser<List<A>> get many1 => this >> (x) => _many(() => [x]);
/**
* Parses [this] zero or more time, skipping its result.
*
* Equivalent to [:this.many > success(null):] but more efficient.
*/
Parser get skipMany {
// Imperative version to avoid stack overflows.
return new Parser((s, pos) {
Position index = pos;
var exps = _emptyExpectation(pos);
bool committed = false;
while(true) {
ParseResult<A> o = this._run(s, index);
exps = exps.best(o.expectations);
committed = committed || o.isCommitted;
if (o.isSuccess) {
index = o.position;
} else if (o.isCommitted) {
return o.copy(expectations: exps);
} else {
return _success(null, s, index, exps, committed);
}
}
});
}
/**
* Parses [this] one or more time, skipping its result.
*
* Equivalent to [:this.many1 > success(null):] but more efficient.
*/
Parser get skipMany1 => this > this.skipMany;
Parser<List<A>> sepBy(Parser sep) => sepBy1(sep).orElse([]);
Parser<List<A>> sepBy1(Parser sep) =>
this >> (x) => (sep > this)._many(() => [x]);
Parser<List<A>> endBy(Parser sep) => (this < sep).many;
Parser<List<A>> endBy1(Parser sep) => (this < sep).many1;
/**
* Parses zero or more occurences of [this] separated and optionally ended
* by [sep].
*/
Parser<List<A>> sepEndBy(Parser sep) => sepEndBy1(sep).orElse([]);
/**
* Parses one or more occurences of [this] separated and optionally ended
* by [sep].
*/
Parser<List<A>> sepEndBy1(Parser sep) => sepBy1(sep) < sep.maybe;
Parser chainl(Parser sep, defaultValue) =>
chainl1(sep) | success(defaultValue);
Parser chainl1(Parser sep) {
rest(acc) {
return new Parser((s, pos) {
Position index = pos;
var exps = _emptyExpectation(pos);
var commit = false;
while(true) {
combine(f) => (x) => f(acc, x);
final res = (success(combine) * sep * this)._run(s, index);
exps = exps.best(res.expectations);
commit = commit || res.isCommitted;
if (res.isSuccess) {
acc = res.value;
index = res.position;
} else if (res.isCommitted) {
return res.copy(expectations: exps);
} else {
return _success(acc, s, index, exps, commit);
}
}
});
}
return this >> rest;
}
/// Warning: may lead to stack overflows.
Parser chainr(Parser sep, defaultValue) =>
chainr1(sep) | success(defaultValue);
/// Warning: may lead to stack overflows.
Parser chainr1(Parser sep) {
rest(x) => success((f) => (y) => f(x, y)) * sep * chainr1(sep)
| success(x);
return this >> rest;
}
Parser<A> between(Parser left, Parser right) => left > (this < right);
/// Returns the substring consumed by [this].
Parser<String> get record {
return new Parser((s, pos) {
final result = run(s, pos);
if (result.isSuccess) {
return result.copy(
value: s.substring(pos.offset, result.position.offset));
} else {
return result;
}
});
}
/**
* Returns the value parsed by [this] along with the position at which it
* has been parsed.
*/
Parser<PointedValue<A>> get withPosition {
return new Parser((s, pos) {
return this.map((v) => new PointedValue(v, pos))._run(s, pos);
});
}
}
Constructors
new Parser(ParseResult<A> f(String s, Position pos)) #
Properties
final Parser committed #
Parser get committed {
return new Parser((s, pos) {
final res = _run(s, pos);
return res.copy(isCommitted: true);
});
}
final Parser<A> lookAhead #
Parses without consuming any input.
Used for defining followedBy, which is probably what you're looking for.
Parser<A> get lookAhead {
return new Parser((s, pos) {
ParseResult res = _run(s, pos);
return res.isSuccess
? _success(res.value, s, pos)
: res;
});
}
final Parser notAhead #
Fails if and only if this succeeds on what's ahead.
Used for defining notFollowedBy, which is probably what you're looking for.
Parser get notAhead {
return new Parser((s, pos) {
ParseResult res = _run(s, pos);
return res.isSuccess
? _failure(s, pos)
: _success(null, s, pos);
});
}
final Parser<String> record #
Returns the substring consumed by this.
Parser<String> get record {
return new Parser((s, pos) {
final result = run(s, pos);
if (result.isSuccess) {
return result.copy(
value: s.substring(pos.offset, result.position.offset));
} else {
return result;
}
});
}
final Parser skipMany #
Parses this zero or more time, skipping its result.
Equivalent to this.many > success(null) but more efficient.
Parser get skipMany {
// Imperative version to avoid stack overflows.
return new Parser((s, pos) {
Position index = pos;
var exps = _emptyExpectation(pos);
bool committed = false;
while(true) {
ParseResult<A> o = this._run(s, index);
exps = exps.best(o.expectations);
committed = committed || o.isCommitted;
if (o.isSuccess) {
index = o.position;
} else if (o.isCommitted) {
return o.copy(expectations: exps);
} else {
return _success(null, s, index, exps, committed);
}
}
});
}
final Parser skipMany1 #
Parses this one or more time, skipping its result.
Equivalent to this.many1 > success(null) but more efficient.
Parser get skipMany1 => this > this.skipMany;
final Parser<PointedValue<A>> withPosition #
Returns the value parsed by this along with the position at which it
has been parsed.
Parser<PointedValue<A>> get withPosition {
return new Parser((s, pos) {
return this.map((v) => new PointedValue(v, pos))._run(s, pos);
});
}
Operators
ParserAccumulator2 operator +(Parser p) #
Parser sequencing: creates a parser accumulator.
ParserAccumulator2 operator +(Parser p) => new ParserAccumulator2(this, p);
Parser operator *(Parser p) #
Applicative <*>
Parser operator *(Parser p) => this >> (f) => p >> (x) {
Function ff = f;
return success(ff(x));
};
Parser<A> operator %(String expected) #
Alias for expecting.
Parser<A> operator %(String expected) => this.expecting(expected);
Parser operator |(Parser p) #
Alternative
Parser operator |(Parser p) {
return new Parser((s, pos) {
ParseResult<A> res = _run(s, pos);
if (res.isSuccess || res.isCommitted) {
return res;
} else {
ParseResult res2 = p._run(s, pos);
return res2.copy(
expectations: res.expectations.best(res2.expectations));
}
});
}
Parser operator >>(Parser g(A x)) #
Monadic bind.
Parser operator >>(Parser g(A x)) {
return new Parser((text, pos) {
ParseResult res = _run(text, pos);
if (res.isSuccess) {
final res2 = g(res.value)._run(text, res.position);
return res2.copy(
expectations: res.expectations.best(res2.expectations),
isCommitted: res.isCommitted || res2.isCommitted);
} else {
return res;
}
});
}
Methods
Parser<A> between(Parser left, Parser right) #
Parser<A> between(Parser left, Parser right) => left > (this < right);
Parser chainl(Parser sep, defaultValue) #
Parser chainl(Parser sep, defaultValue) => chainl1(sep) | success(defaultValue);
Parser chainl1(Parser sep) #
Parser chainl1(Parser sep) {
rest(acc) {
return new Parser((s, pos) {
Position index = pos;
var exps = _emptyExpectation(pos);
var commit = false;
while(true) {
combine(f) => (x) => f(acc, x);
final res = (success(combine) * sep * this)._run(s, index);
exps = exps.best(res.expectations);
commit = commit || res.isCommitted;
if (res.isSuccess) {
acc = res.value;
index = res.position;
} else if (res.isCommitted) {
return res.copy(expectations: exps);
} else {
return _success(acc, s, index, exps, commit);
}
}
});
}
return this >> rest;
}
Parser chainr(Parser sep, defaultValue) #
Warning: may lead to stack overflows.
Parser chainr(Parser sep, defaultValue) => chainr1(sep) | success(defaultValue);
Parser chainr1(Parser sep) #
Warning: may lead to stack overflows.
Parser chainr1(Parser sep) {
rest(x) => success((f) => (y) => f(x, y)) * sep * chainr1(sep)
| success(x);
return this >> rest;
}
Parser expecting(String expected) #
Parser expecting(String expected) {
return new Parser((s, pos) {
final res = _run(s, pos);
return res.copy(expectations: _singleExpectation(expected, pos));
});
}
Parser<A> followedBy(Parser p) #
Succeeds if and only if this succeeds and
p succeeds on what remains to
parse without cosuming it.
string("let").followedBy(space)
Parser<A> followedBy(Parser p) => this < p.lookAhead;
Parser<List<A>> manyUntil(Parser end) #
Parses this 0 or more times until
end succeeds.
Returns the list of values returned by this. It is useful for parsing
comments.
string('/*') > anyChar.manyUntil(string('*/'))
The input parsed by
end is consumed. Use end.lookAhead if you don't
want this.
Parser<List<A>> manyUntil(Parser end) {
// Imperative version to avoid stack overflows.
return new Parser((s, pos) {
List res = [];
Position index = pos;
var exps = _emptyExpectation(pos);
bool committed = false;
while(true) {
final endRes = end._run(s, index);
exps = exps.best(endRes.expectations);
if (endRes.isSuccess) {
return endRes.copy(value: res, expectations: exps,
isCommitted: committed);
} else if (!endRes.isCommitted) {
final xRes = this._run(s, index);
exps = exps.best(xRes.expectations);
committed = committed || xRes.isCommitted;
if (xRes.isSuccess) {
res.add(xRes.value);
index = xRes.position;
} else {
return xRes.copy(expectations: exps, isCommitted: committed);
}
} else {
return endRes.copy(expectations: exps, isCommitted: committed);
}
}
});
}
Parser<A> notFollowedBy(Parser p) #
Succeeds if and only if this succeeds and
p fails on what remains to
parse.
string("let").notFollowedBy(alphanum)
Parser<A> notFollowedBy(Parser p) => this < p.notAhead;
A parse(String s) #
A parse(String s) {
ParseResult<A> result = run(s);
if (result.isSuccess) return result.value;
else throw result.errorMessage;
}
ParseResult run(String s, [Position pos = const Position(0,1,1)]) #
ParseResult run(String s, [Position pos = const Position(0, 1, 1)]) => _run(s, pos);
Parser<List<A>> sepBy1(Parser sep) #
Parser<List<A>> sepBy1(Parser sep) => this >> (x) => (sep > this)._many(() => [x]);
Parser<List<A>> sepEndBy(Parser sep) #
Parses zero or more occurences of this separated and optionally ended
by
sep.
Parser<List<A>> sepEndBy(Parser sep) => sepEndBy1(sep).orElse([]);
Parser<List<A>> sepEndBy1(Parser sep) #
Parses one or more occurences of this separated and optionally ended
by
sep.
Parser<List<A>> sepEndBy1(Parser sep) => sepBy1(sep) < sep.maybe;
Parser skipManyUntil(Parser end) #
Parses this 0 or more times until
end succeeds and discards the result.
Equivalent to this.manyUntil(end) > success(null) but faster. The input
parsed by
end is consumed. Use end.lookAhead if you don't want this.
Parser skipManyUntil(Parser end) {
// Imperative version to avoid stack overflows.
return new Parser((s, pos) {
Position index = pos;
var exps = _emptyExpectation(pos);
var commit = false;
while(true) {
final endRes = end._run(s, index);
exps = exps.best(endRes.expectations);
commit = commit || endRes.isCommitted;
if (endRes.isSuccess) {
return endRes.copy(value: null, expectations: exps,
isCommitted: commit);
} else if (!endRes.isCommitted) {
final xRes = this._run(s, index);
exps = exps.best(xRes.expectations);
commit = commit || xRes.isCommitted;
if (xRes.isSuccess) {
index = xRes.position;
} else {
return xRes.copy(expectations: exps, isCommitted: commit);
}
} else {
return endRes.copy(expectations: exps);
}
}
});
}