Updated combinator

This commit is contained in:
thomashii@dukefirehawk.com 2021-05-07 12:42:02 +08:00
parent 7adeff9a63
commit c2c9e40836
21 changed files with 82 additions and 79 deletions

View file

@ -1,7 +1,7 @@
part of lex.src.combinator;
class _Cache<T> extends Parser<T> {
final Map<int, ParseResult<T>?> _cache = {};
final Map<int, ParseResult<T>> _cache = {};
final Parser<T> parser;
_Cache(this.parser);
@ -10,7 +10,7 @@ class _Cache<T> extends Parser<T> {
ParseResult<T> __parse(ParseArgs args) {
return _cache.putIfAbsent(args.scanner.position, () {
return parser._parse(args.increaseDepth());
})!.change(parser: this);
}).change(parser: this);
}
@override

View file

@ -8,7 +8,7 @@ class _Cast<T, U extends T> extends Parser<U> {
@override
ParseResult<U> __parse(ParseArgs args) {
var result = parser._parse(args.increaseDepth());
return new ParseResult<U>(
return ParseResult<U>(
args.trampoline,
args.scanner,
this,
@ -39,7 +39,7 @@ class _CastDynamic<T> extends Parser<dynamic> {
@override
ParseResult<dynamic> __parse(ParseArgs args) {
var result = parser._parse(args.increaseDepth());
return new ParseResult<dynamic>(
return ParseResult<dynamic>(
args.trampoline,
args.scanner,
this,

View file

@ -5,7 +5,7 @@ part of lex.src.combinator;
/// If [failFast] is `true` (default), then the first failure to parse will abort the parse.
ListParser<T> chain<T>(Iterable<Parser<T>> parsers,
{bool failFast: true, SyntaxErrorSeverity? severity}) {
return new _Chain<T>(
return _Chain<T>(
parsers, failFast != false, severity ?? SyntaxErrorSeverity.error);
}
@ -22,7 +22,7 @@ class _Alt<T> extends Parser<T> {
return result.successful
? result
: result.addErrors([
new SyntaxError(
SyntaxError(
severity, errorMessage, result.span ?? args.scanner.emptySpan),
]);
}
@ -43,8 +43,8 @@ class _Chain<T> extends ListParser<T> {
@override
ParseResult<List<T>> __parse(ParseArgs args) {
var errors = <SyntaxError>[];
var results = <T?>[];
var spans = <FileSpan?>[];
var results = <T>[];
var spans = <FileSpan>[];
bool successful = true;
for (var parser in parsers) {
@ -54,32 +54,36 @@ class _Chain<T> extends ListParser<T> {
if (parser is _Alt) errors.addAll(result.errors);
if (failFast) {
return new ParseResult(
return ParseResult(
args.trampoline, args.scanner, this, false, result.errors);
}
successful = false;
}
results.add(result.value);
if (result.value != null) {
results.add(result.value!);
}
if (result.span != null) spans.add(result.span);
if (result.span != null) {
spans.add(result.span!);
}
}
FileSpan? span;
if (spans.isNotEmpty) {
span = spans.reduce((a, b) => a!.expand(b!));
span = spans.reduce((a, b) => a.expand(b));
}
return new ParseResult<List<T>>(
return ParseResult<List<T>>(
args.trampoline,
args.scanner,
this,
successful,
errors,
span: span,
value: new List<T>.unmodifiable(results),
value: List<T>.unmodifiable(results),
);
}

View file

@ -16,11 +16,10 @@ class _Check<T> extends Parser<T> {
return result;
else if (!matcher.matches(result.value, matchState)) {
return result.change(successful: false).addErrors([
new SyntaxError(
SyntaxError(
severity,
errorMessage ??
matcher.describe(new StringDescription('Expected ')).toString() +
'.',
matcher.describe(StringDescription('Expected ')).toString() + '.',
result.span,
),
]);
@ -30,7 +29,7 @@ class _Check<T> extends Parser<T> {
@override
void stringify(CodeBuffer buffer) {
var d = matcher.describe(new StringDescription());
var d = matcher.describe(StringDescription());
buffer
..writeln('check($d) (')
..indent();

View file

@ -249,7 +249,7 @@ abstract class Parser<T> {
ListParser<dynamic> then(Parser other) => chain<dynamic>([this, other]);
/// Casts this instance into a [ListParser].
ListParser<T?> toList() => _ToList<T>(this);
ListParser<T> toList() => _ToList<T>(this);
/// Consumes and ignores any trailing occurrences of [pattern].
Parser<T> trail(Pattern pattern) =>
@ -371,7 +371,7 @@ class ParseResult<T> {
ParseResult<T> change(
{Parser<T>? parser,
bool? successful,
Iterable<SyntaxError>? errors,
Iterable<SyntaxError> errors = const [],
FileSpan? span,
T? value}) {
return ParseResult<T>(
@ -379,7 +379,7 @@ class ParseResult<T> {
scanner,
parser ?? this.parser,
successful ?? this.successful,
errors ?? this.errors,
errors,
span: span ?? this.span,
value: value ?? this.value,
);

View file

@ -13,8 +13,8 @@ class _Compare<T> extends ListParser<T> {
result = result.change(
value: result.value?.isNotEmpty == true ? result.value : []);
result = result.change(value: new List<T>.from(result.value!));
return new ParseResult<List<T>>(
result = result.change(value: List<T>.from(result.value!));
return ParseResult<List<T>>(
args.trampoline,
args.scanner,
this,

View file

@ -14,7 +14,7 @@ class _Index<T> extends Parser<T> {
if (result.successful)
value = index == -1 ? result.value!.last : result.value!.elementAt(index);
return new ParseResult<T>(
return ParseResult<T>(
args.trampoline,
args.scanner,
this,

View file

@ -5,8 +5,7 @@ part of lex.src.combinator;
/// You can provide a custom [errorMessage].
Parser<T> longest<T>(Iterable<Parser<T>> parsers,
{Object? errorMessage, SyntaxErrorSeverity? severity}) {
return new _Longest(
parsers, errorMessage, severity ?? SyntaxErrorSeverity.error);
return _Longest(parsers, errorMessage, severity ?? SyntaxErrorSeverity.error);
}
class _Longest<T> extends Parser<T> {
@ -23,7 +22,7 @@ class _Longest<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, []);
}
int replay = args.scanner.position;
@ -48,7 +47,7 @@ class _Longest<T> extends Parser<T> {
if (errorMessage != false)
errors.add(
new SyntaxError(
SyntaxError(
severity,
errorMessage?.toString() ??
'No match found for ${parsers.length} alternative(s)',
@ -56,7 +55,7 @@ class _Longest<T> extends Parser<T> {
),
);
return new ParseResult(args.trampoline, args.scanner, this, false, errors);
return ParseResult(args.trampoline, args.scanner, this, false, errors);
}
@override
@ -82,7 +81,7 @@ class _Longest<T> extends Parser<T> {
}
errors.add(
new SyntaxError(
SyntaxError(
severity,
errorMessage?.toString() ??
'No match found for ${parsers.length} alternative(s)',
@ -90,7 +89,7 @@ class _Longest<T> extends Parser<T> {
),
);
return new ParseResult(args.trampoline, args.scanner, this, false, errors);
return ParseResult(args.trampoline, args.scanner, this, false, errors);
}
@override

View file

@ -3,7 +3,7 @@ part of lex.src.combinator;
/// Expects to match a given [pattern]. If it is not matched, you can provide a custom [errorMessage].
Parser<T> match<T>(Pattern pattern,
{String? errorMessage, SyntaxErrorSeverity? severity}) =>
new _Match<T>(pattern, errorMessage, severity ?? SyntaxErrorSeverity.error);
_Match<T>(pattern, errorMessage, severity ?? SyntaxErrorSeverity.error);
class _Match<T> extends Parser<T> {
final Pattern pattern;
@ -16,14 +16,14 @@ class _Match<T> extends Parser<T> {
ParseResult<T> __parse(ParseArgs args) {
var scanner = args.scanner;
if (!scanner.scan(pattern))
return new ParseResult(args.trampoline, scanner, this, false, [
new SyntaxError(
return ParseResult(args.trampoline, scanner, this, false, [
SyntaxError(
severity,
errorMessage ?? 'Expected "$pattern".',
scanner.emptySpan,
),
]);
return new ParseResult<T>(
return ParseResult<T>(
args.trampoline,
scanner,
this,

View file

@ -9,7 +9,7 @@ class _MaxDepth<T> extends Parser<T> {
@override
ParseResult<T> __parse(ParseArgs args) {
if (args.depth > cap) {
return new ParseResult<T>(args.trampoline, args.scanner, this, false, []);
return ParseResult<T>(args.trampoline, args.scanner, this, false, []);
}
return parser._parse(args.increaseDepth());

View file

@ -12,7 +12,7 @@ class _Negate<T> extends Parser<T> {
var result = parser._parse(args.increaseDepth()).change(parser: this);
if (!result.successful) {
return new ParseResult<T>(
return ParseResult<T>(
args.trampoline,
args.scanner,
this,
@ -27,7 +27,7 @@ class _Negate<T> extends Parser<T> {
if (errorMessage != null) {
result = result.addErrors([
new SyntaxError(
SyntaxError(
severity,
errorMessage,
result.span,

View file

@ -13,7 +13,7 @@ class Recursion<T> {
postfix ??= {};
}
Parser<T> precedence(int p) => new _Precedence(this, p);
Parser<T> precedence(int p) => _Precedence(this, p);
void stringify(CodeBuffer buffer) {
buffer
@ -106,7 +106,7 @@ class _Precedence<T> extends Parser<T> {
// If we're not done scanning, then we need some sort of guard to ensure the
// that this exact parser does not run again in the exact position.
}
return new ParseResult(
return ParseResult(
args.trampoline,
args.scanner,
this,
@ -118,7 +118,7 @@ class _Precedence<T> extends Parser<T> {
}
}
return new ParseResult(
return ParseResult(
args.trampoline,
args.scanner,
this,

View file

@ -11,7 +11,7 @@ class _Reduce<T> extends Parser<T> {
ParseResult<List<T>> result = parser._parse(args.increaseDepth());
if (!result.successful)
return new ParseResult<T>(
return ParseResult<T>(
args.trampoline,
args.scanner,
this,
@ -21,7 +21,7 @@ class _Reduce<T> extends Parser<T> {
result = result.change(
value: result.value?.isNotEmpty == true ? result.value : []);
return new ParseResult<T>(
return ParseResult<T>(
args.trampoline,
args.scanner,
this,

View file

@ -1,6 +1,6 @@
part of lex.src.combinator;
Reference<T> reference<T>() => new Reference<T>._();
Reference<T> reference<T>() => Reference<T>._();
class Reference<T> extends Parser<T> {
Parser<T>? _parser;
@ -10,22 +10,21 @@ class Reference<T> extends Parser<T> {
void set parser(Parser<T> value) {
if (_parser != null)
throw new StateError(
'There is already a parser assigned to this reference.');
throw StateError('There is already a parser assigned to this reference.');
_parser = value;
}
@override
ParseResult<T> __parse(ParseArgs args) {
if (_parser == null)
throw new StateError('There is no parser assigned to this reference.');
throw StateError('There is no parser assigned to this reference.');
return _parser!._parse(args);
}
@override
ParseResult<T> _parse(ParseArgs args) {
if (_parser == null)
throw new StateError('There is no parser assigned to this reference.');
throw StateError('There is no parser assigned to this reference.');
return _parser!._parse(args);
}

View file

@ -22,15 +22,14 @@ class _Safe<T> extends Parser<T> {
var errors = <SyntaxError>[];
errors.add(
new SyntaxError(
SyntaxError(
severity,
errorMessage,
args.scanner.lastSpan ?? args.scanner.emptySpan,
),
);
return new ParseResult<T>(
args.trampoline, args.scanner, this, false, errors);
return ParseResult<T>(args.trampoline, args.scanner, this, false, errors);
}
}

View file

@ -1,26 +1,30 @@
part of lex.src.combinator;
class _ToList<T> extends ListParser<T?> {
class _ToList<T> extends ListParser<T> {
final Parser<T> parser;
_ToList(this.parser);
@override
ParseResult<List<T?>> __parse(ParseArgs args) {
ParseResult<List<T>> __parse(ParseArgs args) {
var result = parser._parse(args.increaseDepth());
if (result.value is List) {
return (result as ParseResult<List<T?>>).change(parser: this);
return (result as ParseResult<List<T>>).change(parser: this);
}
return new ParseResult(
List<T> values = [];
if (result.value != null) {
values.add(result.value!);
}
return ParseResult(
args.trampoline,
args.scanner,
this,
result.successful,
result.errors,
span: result.span,
value: [result.value],
value: values,
);
}

View file

@ -1,48 +1,48 @@
part of lex.src.combinator;
/// A typed parser that parses a sequence of 2 values of different types.
Parser<Tuple2<A?, B?>> tuple2<A, B>(Parser<A> a, Parser<B> b) {
Parser<Tuple2<A, B>> tuple2<A, B>(Parser<A> a, Parser<B> b) {
return chain([a, b]).map((r) {
return Tuple2(r.value![0] as A?, r.value![1] as B?);
return Tuple2(r.value?[0] as A, r.value?[1] as B);
});
}
/// A typed parser that parses a sequence of 3 values of different types.
Parser<Tuple3<A?, B?, C?>> tuple3<A, B, C>(Parser<A> a, Parser<B> b, Parser<C> c) {
Parser<Tuple3<A, B, C>> tuple3<A, B, C>(Parser<A> a, Parser<B> b, Parser<C> c) {
return chain([a, b, c]).map((r) {
return Tuple3(r.value![0] as A?, r.value![1] as B?, r.value![2] as C?);
return Tuple3(r.value?[0] as A, r.value?[1] as B, r.value?[2] as C);
});
}
/// A typed parser that parses a sequence of 4 values of different types.
Parser<Tuple4<A?, B?, C?, D?>> tuple4<A, B, C, D>(
Parser<Tuple4<A, B, C, D>> tuple4<A, B, C, D>(
Parser<A> a, Parser<B> b, Parser<C> c, Parser<D> d) {
return chain([a, b, c, d]).map((r) {
return Tuple4(
r.value![0] as A?, r.value![1] as B?, r.value![2] as C?, r.value![3] as D?);
r.value?[0] as A, r.value?[1] as B, r.value?[2] as C, r.value?[3] as D);
});
}
/// A typed parser that parses a sequence of 5 values of different types.
Parser<Tuple5<A?, B?, C?, D?, E?>> tuple5<A, B, C, D, E>(
Parser<Tuple5<A, B, C, D, E>> tuple5<A, B, C, D, E>(
Parser<A> a, Parser<B> b, Parser<C> c, Parser<D> d, Parser<E> e) {
return chain([a, b, c, d, e]).map((r) {
return Tuple5(r.value![0] as A?, r.value![1] as B?, r.value![2] as C?,
r.value![3] as D?, r.value![4] as E?);
return Tuple5(r.value?[0] as A, r.value?[1] as B, r.value?[2] as C,
r.value?[3] as D, r.value?[4] as E);
});
}
/// A typed parser that parses a sequence of 6 values of different types.
Parser<Tuple6<A?, B?, C?, D?, E?, F?>> tuple6<A, B, C, D, E, F>(Parser<A> a,
Parser<Tuple6<A, B, C, D, E, F>> tuple6<A, B, C, D, E, F>(Parser<A> a,
Parser<B> b, Parser<C> c, Parser<D> d, Parser<E> e, Parser<F> f) {
return chain([a, b, c, d, e, f]).map((r) {
return Tuple6(r.value![0] as A?, r.value![1] as B?, r.value![2] as C?,
r.value![3] as D?, r.value![4] as E?, r.value![5] as F?);
return Tuple6(r.value?[0] as A, r.value?[1] as B, r.value?[2] as C,
r.value?[3] as D, r.value?[4] as E, r.value?[5] as F);
});
}
/// A typed parser that parses a sequence of 7 values of different types.
Parser<Tuple7<A?, B?, C?, D?, E?, F?, G?>> tuple7<A, B, C, D, E, F, G>(
Parser<Tuple7<A, B, C, D, E, F, G>> tuple7<A, B, C, D, E, F, G>(
Parser<A> a,
Parser<B> b,
Parser<C> c,
@ -51,7 +51,7 @@ Parser<Tuple7<A?, B?, C?, D?, E?, F?, G?>> tuple7<A, B, C, D, E, F, G>(
Parser<F> f,
Parser<G> g) {
return chain([a, b, c, d, e, f, g]).map((r) {
return Tuple7(r.value![0] as A?, r.value![1] as B?, r.value![2] as C?,
r.value![3] as D?, r.value![4] as E?, r.value![5] as F?, r.value![6] as G?);
return Tuple7(r.value?[0] as A, r.value?[1] as B, r.value?[2] as C,
r.value?[3] as D, r.value?[4] as E, r.value?[5] as F, r.value?[6] as G);
});
}

View file

@ -1,3 +1,3 @@
import 'package:string_scanner/string_scanner.dart';
SpanScanner scan(String text) => new SpanScanner(text);
SpanScanner scan(String text) => SpanScanner(text);

View file

@ -4,7 +4,7 @@ import 'common.dart';
main() {
var number = chain([
match(new RegExp(r'[0-9]+')).value((r) => int.parse(r.span!.text)),
match(RegExp(r'[0-9]+')).value((r) => int.parse(r.span!.text)),
match(',').opt(),
]).first().cast<int>();

View file

@ -19,7 +19,7 @@ main() {
});
test('check', () {
var parser = match<int>(new RegExp(r'[A-Za-z]+'))
var parser = match<int>(RegExp(r'[A-Za-z]+'))
.value((r) => r.span!.length)
.check(greaterThan(3));
expect(parser.parse(scan('helloworld')).successful, isTrue);
@ -27,8 +27,7 @@ main() {
});
test('map', () {
var parser =
match(new RegExp(r'[A-Za-z]+')).map<int>((r) => r.span!.length);
var parser = match(RegExp(r'[A-Za-z]+')).map<int>((r) => r.span!.length);
expect(parser.parse(scan('hello')).value, 5);
});

View file

@ -6,12 +6,12 @@ void main() {}
/*
void main() {
var number = match(new RegExp(r'-?[0-9]+(\.[0-9]+)?'))
var number = match( RegExp(r'-?[0-9]+(\.[0-9]+)?'))
.map<num>((r) => num.parse(r.span.text));
var term = reference<num>();
var r = new Recursion<num>();
var r = Recursion<num>();
r.prefix = [number];
@ -32,7 +32,7 @@ void main() {
term.parser = r.precedence(0);
num parse(String text) {
var scanner = new SpanScanner(text);
var scanner = SpanScanner(text);
var result = term.parse(scanner);
print(result.span.highlight());
return result.value;