Updated combinator

This commit is contained in:
thomashii 2021-05-18 21:15:28 +08:00
parent 082b7876b4
commit 26d9936a3f
26 changed files with 85 additions and 69 deletions

View file

@ -1,3 +1,6 @@
# 2.0.2
* Resolve static analysis warnings
# 2.0.1 # 2.0.1
* Updated README * Updated README

View file

@ -1,5 +1,5 @@
# angel3_combinator # angel3_combinator
[![version](https://img.shields.io/badge/pub-v2.0.1-brightgreen)](https://pub.dartlang.org/packages/angel3_combinator) [![version](https://img.shields.io/badge/pub-v2.0.2-brightgreen)](https://pub.dartlang.org/packages/angel3_combinator)
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety) [![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion) [![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)

View file

@ -1,3 +1,4 @@
include: package:pedantic/analysis_options.yaml
analyzer: analyzer:
strong-mode: strong-mode:
implicit-casts: false implicit-casts: false

View file

@ -49,7 +49,8 @@ void main() {
print(error.toolString); print(error.toolString);
print(error.span!.highlight(color: true)); print(error.span!.highlight(color: true));
} }
} else } else {
print(result.value); print(result.value);
}
} }
} }

View file

@ -64,7 +64,8 @@ void main() {
stderr.writeln(error.toolString); stderr.writeln(error.toolString);
stderr.writeln(error.span!.highlight(color: true)); stderr.writeln(error.span!.highlight(color: true));
} }
} else } else {
print(result.value); print(result.value);
}
} }
} }

View file

@ -10,7 +10,7 @@ final Parser<String> id =
// pattern. // pattern.
// //
// This is useful for parsing arrays or map literals. // This is useful for parsing arrays or map literals.
main() { void main() {
while (true) { while (true) {
stdout.write('Enter a string (ex "a,b,c"): '); stdout.write('Enter a string (ex "a,b,c"): ');
var line = stdin.readLineSync()!; var line = stdin.readLineSync()!;
@ -22,7 +22,8 @@ main() {
print(error.toolString); print(error.toolString);
print(error.span!.highlight(color: true)); print(error.span!.highlight(color: true));
} }
} else } else {
print(result.value); print(result.value);
}
} }
} }

View file

@ -50,7 +50,7 @@ Parser jsonGrammar() {
return expr.foldErrors(); return expr.foldErrors();
} }
main() { void main() {
var JSON = jsonGrammar(); var JSON = jsonGrammar();
while (true) { while (true) {
@ -64,7 +64,8 @@ main() {
print(error.toolString); print(error.toolString);
print(error.span!.highlight(color: true)); print(error.span!.highlight(color: true));
} }
} else } else {
print(result.value); print(result.value);
}
} }
} }

View file

@ -19,7 +19,7 @@ final Parser number = //
(minus.opt() & decimal) // minus?, decimal (minus.opt() & decimal) // minus?, decimal
.map<num>((r) => num.parse(r.span!.text)); .map<num>((r) => num.parse(r.span!.text));
main() { void main() {
while (true) { while (true) {
stdout.write('Enter a number: '); stdout.write('Enter a number: ');
var line = stdin.readLineSync()!; var line = stdin.readLineSync()!;
@ -31,7 +31,8 @@ main() {
stderr.writeln(error.toolString); stderr.writeln(error.toolString);
stderr.writeln(error.span!.highlight(color: true)); stderr.writeln(error.span!.highlight(color: true));
} }
} else } else {
print(result.value); print(result.value);
}
} }
} }

View file

@ -26,7 +26,7 @@ final Parser pairs = pair
final Parser queryString = pairs.opt(); final Parser queryString = pairs.opt();
main() { void main() {
while (true) { while (true) {
stdout.write('Enter a query string: '); stdout.write('Enter a query string: ');
var line = stdin.readLineSync()!; var line = stdin.readLineSync()!;
@ -38,7 +38,8 @@ main() {
print(error.toolString); print(error.toolString);
print(error.span!.highlight(color: true)); print(error.span!.highlight(color: true));
} }
} else } else {
print(result.value); print(result.value);
}
} }
} }

View file

@ -45,12 +45,13 @@ void main() {
while (q.isNotEmpty) { while (q.isNotEmpty) {
var current = q.removeFirst(); var current = q.removeFirst();
if (current is! Tuple2) if (current is! Tuple2) {
out.insert(0, current); out.insert(0, current);
else { } else {
var args = []; var args = [];
for (int i = 0; i < (current.item1 as num); i++) for (var i = 0; i < (current.item1 as num); i++) {
args.add(out.removeLast()); args.add(out.removeLast());
}
out.add(current.item2(args)); out.add(current.item2(args));
} }
} }

View file

@ -7,7 +7,7 @@ part of lex.src.combinator;
/// You can provide a custom [errorMessage]. You can set it to `false` to not /// You can provide a custom [errorMessage]. You can set it to `false` to not
/// generate any error at all. /// generate any error at all.
Parser<T> any<T>(Iterable<Parser<T>> parsers, Parser<T> any<T>(Iterable<Parser<T>> parsers,
{bool backtrack: true, errorMessage, SyntaxErrorSeverity? severity}) { {bool backtrack = true, errorMessage, SyntaxErrorSeverity? severity}) {
return _Any(parsers, backtrack != false, errorMessage, return _Any(parsers, backtrack != false, errorMessage,
severity ?? SyntaxErrorSeverity.error); severity ?? SyntaxErrorSeverity.error);
} }
@ -30,14 +30,14 @@ class _Any<T> extends Parser<T> {
} }
var errors = <SyntaxError>[]; var errors = <SyntaxError>[];
int replay = args.scanner.position; var replay = args.scanner.position;
for (var parser in inactive) { for (var parser in inactive) {
var result = parser._parse(args.increaseDepth()); var result = parser._parse(args.increaseDepth());
if (result.successful) if (result.successful) {
return result; return result;
else { } else {
if (backtrack) args.scanner.position = replay; if (backtrack) args.scanner.position = replay;
if (parser is _Alt) errors.addAll(result.errors); if (parser is _Alt) errors.addAll(result.errors);
} }
@ -60,7 +60,7 @@ class _Any<T> extends Parser<T> {
@override @override
ParseResult<T> __parse(ParseArgs args) { ParseResult<T> __parse(ParseArgs args) {
// Never called // Never called
throw ArgumentError("[Combinator] Invalid method call"); throw ArgumentError('[Combinator] Invalid method call');
} }
@override @override
@ -68,7 +68,7 @@ class _Any<T> extends Parser<T> {
buffer buffer
..writeln('any(${parsers.length}) (') ..writeln('any(${parsers.length}) (')
..indent(); ..indent();
int i = 1; var i = 1;
for (var parser in parsers) { for (var parser in parsers) {
buffer buffer

View file

@ -4,7 +4,7 @@ part of lex.src.combinator;
/// ///
/// If [failFast] is `true` (default), then the first failure to parse will abort the parse. /// If [failFast] is `true` (default), then the first failure to parse will abort the parse.
ListParser<T> chain<T>(Iterable<Parser<T>> parsers, ListParser<T> chain<T>(Iterable<Parser<T>> parsers,
{bool failFast: true, SyntaxErrorSeverity? severity}) { {bool failFast = true, SyntaxErrorSeverity? severity}) {
return _Chain<T>( return _Chain<T>(
parsers, failFast != false, severity ?? SyntaxErrorSeverity.error); parsers, failFast != false, severity ?? SyntaxErrorSeverity.error);
} }
@ -45,7 +45,7 @@ class _Chain<T> extends ListParser<T> {
var errors = <SyntaxError>[]; var errors = <SyntaxError>[];
var results = <T>[]; var results = <T>[];
var spans = <FileSpan>[]; var spans = <FileSpan>[];
bool successful = true; var successful = true;
for (var parser in parsers) { for (var parser in parsers) {
var result = parser._parse(args.increaseDepth()); var result = parser._parse(args.increaseDepth());
@ -64,7 +64,7 @@ class _Chain<T> extends ListParser<T> {
if (result.value != null) { if (result.value != null) {
results.add(result.value!); results.add(result.value!);
} else { } else {
results.add("NULL" as T); results.add('NULL' as T);
} }
if (result.span != null) { if (result.span != null) {
@ -94,7 +94,7 @@ class _Chain<T> extends ListParser<T> {
buffer buffer
..writeln('chain(${parsers.length}) (') ..writeln('chain(${parsers.length}) (')
..indent(); ..indent();
int i = 1; var i = 1;
for (var parser in parsers) { for (var parser in parsers) {
buffer buffer

View file

@ -12,9 +12,9 @@ class _Check<T> extends Parser<T> {
ParseResult<T> __parse(ParseArgs args) { ParseResult<T> __parse(ParseArgs args) {
var matchState = {}; var matchState = {};
var result = parser._parse(args.increaseDepth()).change(parser: this); var result = parser._parse(args.increaseDepth()).change(parser: this);
if (!result.successful) if (!result.successful) {
return result; return result;
else if (!matcher.matches(result.value, matchState)) { } else if (!matcher.matches(result.value, matchState)) {
return result.change(successful: false).addErrors([ return result.change(successful: false).addErrors([
SyntaxError( SyntaxError(
severity, severity,
@ -23,8 +23,9 @@ class _Check<T> extends Parser<T> {
result.span, result.span,
), ),
]); ]);
} else } else {
return result; return result;
}
} }
@override @override

View file

@ -72,11 +72,13 @@ abstract class Parser<T> {
ParseResult<T> _parse(ParseArgs args) { ParseResult<T> _parse(ParseArgs args) {
var pos = args.scanner.position; var pos = args.scanner.position;
if (args.trampoline.hasMemoized(this, pos)) if (args.trampoline.hasMemoized(this, pos)) {
return args.trampoline.getMemoized<T>(this, pos); return args.trampoline.getMemoized<T>(this, pos);
}
if (args.trampoline.isActive(this, pos)) if (args.trampoline.isActive(this, pos)) {
return ParseResult(args.trampoline, args.scanner, this, false, []); return ParseResult(args.trampoline, args.scanner, this, false, []);
}
args.trampoline.enter(this, pos); args.trampoline.enter(this, pos);
var result = __parse(args); var result = __parse(args);
@ -103,7 +105,6 @@ abstract class Parser<T> {
/// Casts this parser to produce [dynamic] objects. /// Casts this parser to produce [dynamic] objects.
Parser<dynamic> castDynamic() => _CastDynamic<T>(this); Parser<dynamic> castDynamic() => _CastDynamic<T>(this);
// TODO: Type issue
/// Runs the given function, which changes the returned [ParseResult] into one relating to a [U] object. /// 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); return _Change<T, U>(this, f);
@ -122,7 +123,7 @@ abstract class Parser<T> {
_Alt<T>(this, errorMessage, severity ?? SyntaxErrorSeverity.error); _Alt<T>(this, errorMessage, severity ?? SyntaxErrorSeverity.error);
/// Removes multiple errors that occur in the same spot; this can reduce noise in parser output. /// Removes multiple errors that occur in the same spot; this can reduce noise in parser output.
Parser<T> foldErrors({bool equal(SyntaxError a, SyntaxError b)?}) { Parser<T> foldErrors({bool Function(SyntaxError a, SyntaxError b)? equal}) {
equal ??= (b, e) => b.span?.start.offset == e.span?.start.offset; equal ??= (b, e) => b.span?.start.offset == e.span?.start.offset;
return _FoldErrors<T>(this, equal); return _FoldErrors<T>(this, equal);
} }
@ -177,8 +178,8 @@ abstract class Parser<T> {
/// ///
/// The generated parser only runs once; repeated uses always exit eagerly. /// The generated parser only runs once; repeated uses always exit eagerly.
Parser<T> safe( Parser<T> safe(
{bool backtrack: true, {bool backtrack = true,
String errorMessage = "error", String errorMessage = 'error',
SyntaxErrorSeverity? severity}) => SyntaxErrorSeverity? severity}) =>
_Safe<T>( _Safe<T>(
this, backtrack, errorMessage, severity ?? SyntaxErrorSeverity.error); this, backtrack, errorMessage, severity ?? SyntaxErrorSeverity.error);
@ -191,14 +192,14 @@ abstract class Parser<T> {
/// Use this as a shortcut to parse arrays, parameter lists, etc. /// Use this as a shortcut to parse arrays, parameter lists, etc.
Parser<List<T>> separatedBy(Parser other) { Parser<List<T>> separatedBy(Parser other) {
var suffix = other.then(this).index(1).cast<T>(); var suffix = other.then(this).index(1).cast<T>();
return this.then(suffix.star()).map((r) { return then(suffix.star()).map((r) {
var v = r.value; var v = r.value;
if (v == null || v.length < 2) { if (v == null || v.length < 2) {
return []; return [];
} }
var preceding = v.isEmpty ? [] : (v[0] == null ? [] : [v[0]]); var preceding = v.isEmpty ? [] : (v[0] == null ? [] : [v[0]]);
var out = List<T>.from(preceding); var out = List<T>.from(preceding);
if (v[1] != null && v[1] != "NULL") { if (v[1] != null && v[1] != 'NULL') {
v[1].forEach((element) { v[1].forEach((element) {
out.add(element as T); out.add(element as T);
}); });
@ -240,7 +241,7 @@ abstract class Parser<T> {
Parser<T> space() => trail(RegExp(r'[ \n\r\t]+')); Parser<T> space() => trail(RegExp(r'[ \n\r\t]+'));
/// Consumes 0 or more instance(s) of this parser. /// 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(); times(1, exact: false, backtrack: backtrack).opt();
/// Shortcut for [chain]-ing two parsers together. /// Shortcut for [chain]-ing two parsers together.
@ -260,10 +261,10 @@ abstract class Parser<T> {
/// ///
/// You can provide custom error messages for when there are [tooFew] or [tooMany] occurrences. /// 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, {bool exact = true,
String tooFew = 'Too few', String tooFew = 'Too few',
String tooMany = 'Too many', String tooMany = 'Too many',
bool backtrack: true, bool backtrack = true,
SyntaxErrorSeverity? severity}) { SyntaxErrorSeverity? severity}) {
return _Repeat<T>(this, count, exact, tooFew, tooMany, backtrack, return _Repeat<T>(this, count, exact, tooFew, tooMany, backtrack,
severity ?? SyntaxErrorSeverity.error); severity ?? SyntaxErrorSeverity.error);
@ -273,7 +274,7 @@ abstract class Parser<T> {
/// ///
/// If [backtrack] is `true` (default), then a failed parse will not /// If [backtrack] is `true` (default), then a failed parse will not
/// modify the scanner state. /// modify the scanner state.
Parser<T> opt({bool backtrack: true}) => _Opt(this, backtrack); Parser<T> opt({bool backtrack = true}) => _Opt(this, backtrack);
/// Sets the value of the [ParseResult]. /// Sets the value of the [ParseResult].
Parser<T> value(T Function(ParseResult<T?>) f) { Parser<T> value(T Function(ParseResult<T?>) f) {
@ -302,7 +303,7 @@ abstract class ListParser<T> extends Parser<List<T>> {
ListParser<T> sort(Comparator<T> compare) => _Compare(this, compare); ListParser<T> sort(Comparator<T> compare) => _Compare(this, compare);
@override @override
ListParser<T> opt({bool backtrack: true}) => _ListOpt(this, backtrack); ListParser<T> opt({bool backtrack = true}) => _ListOpt(this, backtrack);
/// Modifies this parser, returning only the values that match a predicate. /// Modifies this parser, returning only the values that match a predicate.
Parser<List<T>> where(bool Function(T) f) => Parser<List<T>> where(bool Function(T) f) =>

View file

@ -8,7 +8,7 @@ class _Compare<T> extends ListParser<T> {
@override @override
ParseResult<List<T>> __parse(ParseArgs args) { ParseResult<List<T>> __parse(ParseArgs args) {
ParseResult<List<T>> result = parser._parse(args.increaseDepth()); var result = parser._parse(args.increaseDepth());
if (!result.successful) return result; if (!result.successful) return result;
result = result.change( result = result.change(

View file

@ -8,19 +8,18 @@ class _Index<T> extends Parser<T> {
@override @override
ParseResult<T> __parse(ParseArgs args) { ParseResult<T> __parse(ParseArgs args) {
ParseResult<List<T>> result = parser._parse(args.increaseDepth()); var result = parser._parse(args.increaseDepth());
Object? value; Object? value;
if (result.successful) { if (result.successful) {
var vList = result.value; var vList = result.value;
if (vList == null) { if (vList == null) {
throw ArgumentError("ParseResult is null"); throw ArgumentError('ParseResult is null');
} }
if (index == -1) { if (index == -1) {
value = vList.last; value = vList.last;
} else { } else {
if (index < vList.length) { if (index < vList.length) {
//TODO: Look at this
// print(">>>>Index: $index, Size: ${vList.length}"); // print(">>>>Index: $index, Size: ${vList.length}");
// value = // value =
// index == -1 ? result.value!.last : result.value!.elementAt(index); // index == -1 ? result.value!.last : result.value!.elementAt(index);

View file

@ -25,16 +25,16 @@ class _Longest<T> extends Parser<T> {
return ParseResult(args.trampoline, args.scanner, this, false, []); return ParseResult(args.trampoline, args.scanner, this, false, []);
} }
int replay = args.scanner.position; var replay = args.scanner.position;
var errors = <SyntaxError>[]; var errors = <SyntaxError>[];
var results = <ParseResult<T>>[]; var results = <ParseResult<T>>[];
for (var parser in inactive) { for (var parser in inactive) {
var result = parser._parse(args.increaseDepth()); var result = parser._parse(args.increaseDepth());
if (result.successful && result.span != null) if (result.successful && result.span != null) {
results.add(result); results.add(result);
else if (parser is _Alt) errors.addAll(result.errors); } else if (parser is _Alt) errors.addAll(result.errors);
args.scanner.position = replay; args.scanner.position = replay;
} }
@ -45,7 +45,7 @@ class _Longest<T> extends Parser<T> {
return results.first; return results.first;
} }
if (errorMessage != false) if (errorMessage != false) {
errors.add( errors.add(
SyntaxError( SyntaxError(
severity, severity,
@ -54,22 +54,23 @@ class _Longest<T> extends Parser<T> {
args.scanner.emptySpan, args.scanner.emptySpan,
), ),
); );
}
return ParseResult(args.trampoline, args.scanner, this, false, errors); return ParseResult(args.trampoline, args.scanner, this, false, errors);
} }
@override @override
ParseResult<T> __parse(ParseArgs args) { ParseResult<T> __parse(ParseArgs args) {
int replay = args.scanner.position; var replay = args.scanner.position;
var errors = <SyntaxError>[]; var errors = <SyntaxError>[];
var results = <ParseResult<T>>[]; var results = <ParseResult<T>>[];
for (var parser in parsers) { for (var parser in parsers) {
var result = parser._parse(args.increaseDepth()); var result = parser._parse(args.increaseDepth());
if (result.successful) if (result.successful) {
results.add(result); results.add(result);
else if (parser is _Alt) errors.addAll(result.errors); } else if (parser is _Alt) errors.addAll(result.errors);
args.scanner.position = replay; args.scanner.position = replay;
} }
@ -97,7 +98,7 @@ class _Longest<T> extends Parser<T> {
buffer buffer
..writeln('longest(${parsers.length}) (') ..writeln('longest(${parsers.length}) (')
..indent(); ..indent();
int i = 1; var i = 1;
for (var parser in parsers) { for (var parser in parsers) {
buffer buffer

View file

@ -15,7 +15,7 @@ class _Match<T> extends Parser<T> {
@override @override
ParseResult<T> __parse(ParseArgs args) { ParseResult<T> __parse(ParseArgs args) {
var scanner = args.scanner; var scanner = args.scanner;
if (!scanner.scan(pattern)) if (!scanner.scan(pattern)) {
return ParseResult(args.trampoline, scanner, this, false, [ return ParseResult(args.trampoline, scanner, this, false, [
SyntaxError( SyntaxError(
severity, severity,
@ -23,6 +23,7 @@ class _Match<T> extends Parser<T> {
scanner.emptySpan, scanner.emptySpan,
), ),
]); ]);
}
return ParseResult<T>( return ParseResult<T>(
args.trampoline, args.trampoline,
scanner, scanner,

View file

@ -37,7 +37,7 @@ class _ListOpt<T> extends ListParser<T> {
@override @override
ParseResult<List<T>> __parse(ParseArgs args) { ParseResult<List<T>> __parse(ParseArgs args) {
var replay = args.scanner.position; var replay = args.scanner.position;
ParseResult<List<T>> result = parser._parse(args.increaseDepth()); var result = parser._parse(args.increaseDepth());
if (!result.successful) args.scanner.position = replay; if (!result.successful) args.scanner.position = replay;

View file

@ -8,9 +8,9 @@ class _Reduce<T> extends Parser<T> {
@override @override
ParseResult<T> __parse(ParseArgs args) { ParseResult<T> __parse(ParseArgs args) {
ParseResult<List<T>> result = parser._parse(args.increaseDepth()); var result = parser._parse(args.increaseDepth());
if (!result.successful) if (!result.successful) {
return ParseResult<T>( return ParseResult<T>(
args.trampoline, args.trampoline,
args.scanner, args.scanner,
@ -18,6 +18,7 @@ class _Reduce<T> extends Parser<T> {
false, false,
result.errors, result.errors,
); );
}
result = result.change( result = result.change(
value: result.value?.isNotEmpty == true ? result.value : []); value: result.value?.isNotEmpty == true ? result.value : []);

View file

@ -8,23 +8,26 @@ class Reference<T> extends Parser<T> {
Reference._(); Reference._();
void set parser(Parser<T> value) { set parser(Parser<T> value) {
if (_parser != null) if (_parser != null) {
throw StateError('There is already a parser assigned to this reference.'); throw StateError('There is already a parser assigned to this reference.');
}
_parser = value; _parser = value;
} }
@override @override
ParseResult<T> __parse(ParseArgs args) { ParseResult<T> __parse(ParseArgs args) {
if (_parser == null) if (_parser == null) {
throw StateError('There is no parser assigned to this reference.'); throw StateError('There is no parser assigned to this reference.');
}
return _parser!._parse(args); return _parser!._parse(args);
} }
@override @override
ParseResult<T> _parse(ParseArgs args) { ParseResult<T> _parse(ParseArgs args) {
if (_parser == null) if (_parser == null) {
throw StateError('There is no parser assigned to this reference.'); throw StateError('There is no parser assigned to this reference.');
}
return _parser!._parse(args); return _parser!._parse(args);
} }

View file

@ -16,7 +16,8 @@ class _Repeat<T> extends ListParser<T> {
var errors = <SyntaxError>[]; var errors = <SyntaxError>[];
var results = <T>[]; var results = <T>[];
var spans = <FileSpan>[]; var spans = <FileSpan>[];
int success = 0, replay = args.scanner.position; var success = 0;
var replay = args.scanner.position;
ParseResult<T> result; ParseResult<T> result;
do { do {

View file

@ -13,7 +13,7 @@ class _ToList<T> extends ListParser<T> {
return (result as ParseResult<List<T>>).change(parser: this); return (result as ParseResult<List<T>>).change(parser: this);
} }
List<T> values = []; var values = <T>[];
if (result.value != null) { if (result.value != null) {
values.add(result.value!); values.add(result.value!);
} }

View file

@ -1,5 +1,5 @@
name: angel3_combinator name: angel3_combinator
version: 2.0.1 version: 2.0.2
description: Packrat parser combinators that support static typing, generics, file spans, memoization, and more. description: Packrat parser combinators that support static typing, generics, file spans, memoization, and more.
homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/combinator homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/combinator
environment: environment:
@ -12,3 +12,4 @@ dependencies:
tuple: ^2.0.0 tuple: ^2.0.0
dev_dependencies: dev_dependencies:
test: ^1.17.4 test: ^1.17.4
pedantic: ^1.11.0

View file

@ -2,7 +2,7 @@ import 'package:angel3_combinator/angel3_combinator.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'common.dart'; import 'common.dart';
main() { void main() {
var number = chain([ var number = chain([
match(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(), match(',').opt(),

View file

@ -1,7 +1,3 @@
import 'package:angel3_combinator/angel3_combinator.dart';
import 'package:string_scanner/string_scanner.dart';
import 'package:test/test.dart';
void main() {} void main() {}
/* /*