Updated combinator
This commit is contained in:
parent
082b7876b4
commit
26d9936a3f
26 changed files with 85 additions and 69 deletions
|
@ -1,3 +1,6 @@
|
|||
# 2.0.2
|
||||
* Resolve static analysis warnings
|
||||
|
||||
# 2.0.1
|
||||
* Updated README
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# 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)
|
||||
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
analyzer:
|
||||
strong-mode:
|
||||
implicit-casts: false
|
||||
|
|
|
@ -49,7 +49,8 @@ void main() {
|
|||
print(error.toolString);
|
||||
print(error.span!.highlight(color: true));
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
print(result.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,8 @@ void main() {
|
|||
stderr.writeln(error.toolString);
|
||||
stderr.writeln(error.span!.highlight(color: true));
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
print(result.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ final Parser<String> id =
|
|||
// pattern.
|
||||
//
|
||||
// This is useful for parsing arrays or map literals.
|
||||
main() {
|
||||
void main() {
|
||||
while (true) {
|
||||
stdout.write('Enter a string (ex "a,b,c"): ');
|
||||
var line = stdin.readLineSync()!;
|
||||
|
@ -22,7 +22,8 @@ main() {
|
|||
print(error.toolString);
|
||||
print(error.span!.highlight(color: true));
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
print(result.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ Parser jsonGrammar() {
|
|||
return expr.foldErrors();
|
||||
}
|
||||
|
||||
main() {
|
||||
void main() {
|
||||
var JSON = jsonGrammar();
|
||||
|
||||
while (true) {
|
||||
|
@ -64,7 +64,8 @@ main() {
|
|||
print(error.toolString);
|
||||
print(error.span!.highlight(color: true));
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
print(result.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ final Parser number = //
|
|||
(minus.opt() & decimal) // minus?, decimal
|
||||
.map<num>((r) => num.parse(r.span!.text));
|
||||
|
||||
main() {
|
||||
void main() {
|
||||
while (true) {
|
||||
stdout.write('Enter a number: ');
|
||||
var line = stdin.readLineSync()!;
|
||||
|
@ -31,7 +31,8 @@ main() {
|
|||
stderr.writeln(error.toolString);
|
||||
stderr.writeln(error.span!.highlight(color: true));
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
print(result.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ final Parser pairs = pair
|
|||
|
||||
final Parser queryString = pairs.opt();
|
||||
|
||||
main() {
|
||||
void main() {
|
||||
while (true) {
|
||||
stdout.write('Enter a query string: ');
|
||||
var line = stdin.readLineSync()!;
|
||||
|
@ -38,7 +38,8 @@ main() {
|
|||
print(error.toolString);
|
||||
print(error.span!.highlight(color: true));
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
print(result.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,12 +45,13 @@ void main() {
|
|||
|
||||
while (q.isNotEmpty) {
|
||||
var current = q.removeFirst();
|
||||
if (current is! Tuple2)
|
||||
if (current is! Tuple2) {
|
||||
out.insert(0, current);
|
||||
else {
|
||||
} else {
|
||||
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());
|
||||
}
|
||||
out.add(current.item2(args));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ part of lex.src.combinator;
|
|||
/// You can provide a custom [errorMessage]. You can set it to `false` to not
|
||||
/// generate any error at all.
|
||||
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,
|
||||
severity ?? SyntaxErrorSeverity.error);
|
||||
}
|
||||
|
@ -30,14 +30,14 @@ class _Any<T> extends Parser<T> {
|
|||
}
|
||||
|
||||
var errors = <SyntaxError>[];
|
||||
int replay = args.scanner.position;
|
||||
var replay = args.scanner.position;
|
||||
|
||||
for (var parser in inactive) {
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
|
||||
if (result.successful)
|
||||
if (result.successful) {
|
||||
return result;
|
||||
else {
|
||||
} else {
|
||||
if (backtrack) args.scanner.position = replay;
|
||||
if (parser is _Alt) errors.addAll(result.errors);
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ class _Any<T> extends Parser<T> {
|
|||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
// Never called
|
||||
throw ArgumentError("[Combinator] Invalid method call");
|
||||
throw ArgumentError('[Combinator] Invalid method call');
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -68,7 +68,7 @@ class _Any<T> extends Parser<T> {
|
|||
buffer
|
||||
..writeln('any(${parsers.length}) (')
|
||||
..indent();
|
||||
int i = 1;
|
||||
var i = 1;
|
||||
|
||||
for (var parser in parsers) {
|
||||
buffer
|
||||
|
|
|
@ -4,7 +4,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}) {
|
||||
{bool failFast = true, SyntaxErrorSeverity? severity}) {
|
||||
return _Chain<T>(
|
||||
parsers, failFast != false, severity ?? SyntaxErrorSeverity.error);
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ class _Chain<T> extends ListParser<T> {
|
|||
var errors = <SyntaxError>[];
|
||||
var results = <T>[];
|
||||
var spans = <FileSpan>[];
|
||||
bool successful = true;
|
||||
var successful = true;
|
||||
|
||||
for (var parser in parsers) {
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
|
@ -64,7 +64,7 @@ class _Chain<T> extends ListParser<T> {
|
|||
if (result.value != null) {
|
||||
results.add(result.value!);
|
||||
} else {
|
||||
results.add("NULL" as T);
|
||||
results.add('NULL' as T);
|
||||
}
|
||||
|
||||
if (result.span != null) {
|
||||
|
@ -94,7 +94,7 @@ class _Chain<T> extends ListParser<T> {
|
|||
buffer
|
||||
..writeln('chain(${parsers.length}) (')
|
||||
..indent();
|
||||
int i = 1;
|
||||
var i = 1;
|
||||
|
||||
for (var parser in parsers) {
|
||||
buffer
|
||||
|
|
|
@ -12,9 +12,9 @@ class _Check<T> extends Parser<T> {
|
|||
ParseResult<T> __parse(ParseArgs args) {
|
||||
var matchState = {};
|
||||
var result = parser._parse(args.increaseDepth()).change(parser: this);
|
||||
if (!result.successful)
|
||||
if (!result.successful) {
|
||||
return result;
|
||||
else if (!matcher.matches(result.value, matchState)) {
|
||||
} else if (!matcher.matches(result.value, matchState)) {
|
||||
return result.change(successful: false).addErrors([
|
||||
SyntaxError(
|
||||
severity,
|
||||
|
@ -23,8 +23,9 @@ class _Check<T> extends Parser<T> {
|
|||
result.span,
|
||||
),
|
||||
]);
|
||||
} else
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -72,11 +72,13 @@ abstract class Parser<T> {
|
|||
ParseResult<T> _parse(ParseArgs args) {
|
||||
var pos = args.scanner.position;
|
||||
|
||||
if (args.trampoline.hasMemoized(this, pos))
|
||||
if (args.trampoline.hasMemoized(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, []);
|
||||
}
|
||||
|
||||
args.trampoline.enter(this, pos);
|
||||
var result = __parse(args);
|
||||
|
@ -103,7 +105,6 @@ abstract class Parser<T> {
|
|||
/// Casts this parser to produce [dynamic] objects.
|
||||
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.
|
||||
Parser<U> change<U>(ParseResult<U> Function(ParseResult<T>) f) {
|
||||
return _Change<T, U>(this, f);
|
||||
|
@ -122,7 +123,7 @@ abstract class Parser<T> {
|
|||
_Alt<T>(this, errorMessage, severity ?? SyntaxErrorSeverity.error);
|
||||
|
||||
/// 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;
|
||||
return _FoldErrors<T>(this, equal);
|
||||
}
|
||||
|
@ -177,8 +178,8 @@ abstract class Parser<T> {
|
|||
///
|
||||
/// The generated parser only runs once; repeated uses always exit eagerly.
|
||||
Parser<T> safe(
|
||||
{bool backtrack: true,
|
||||
String errorMessage = "error",
|
||||
{bool backtrack = true,
|
||||
String errorMessage = 'error',
|
||||
SyntaxErrorSeverity? severity}) =>
|
||||
_Safe<T>(
|
||||
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.
|
||||
Parser<List<T>> separatedBy(Parser other) {
|
||||
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;
|
||||
if (v == null || v.length < 2) {
|
||||
return [];
|
||||
}
|
||||
var preceding = v.isEmpty ? [] : (v[0] == null ? [] : [v[0]]);
|
||||
var out = List<T>.from(preceding);
|
||||
if (v[1] != null && v[1] != "NULL") {
|
||||
if (v[1] != null && v[1] != 'NULL') {
|
||||
v[1].forEach((element) {
|
||||
out.add(element as T);
|
||||
});
|
||||
|
@ -240,7 +241,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.
|
||||
|
@ -260,10 +261,10 @@ abstract class Parser<T> {
|
|||
///
|
||||
/// You can provide custom error messages for when there are [tooFew] or [tooMany] occurrences.
|
||||
ListParser<T> times(int count,
|
||||
{bool exact: true,
|
||||
{bool exact = true,
|
||||
String tooFew = 'Too few',
|
||||
String tooMany = 'Too many',
|
||||
bool backtrack: true,
|
||||
bool backtrack = true,
|
||||
SyntaxErrorSeverity? severity}) {
|
||||
return _Repeat<T>(this, count, exact, tooFew, tooMany, backtrack,
|
||||
severity ?? SyntaxErrorSeverity.error);
|
||||
|
@ -273,7 +274,7 @@ abstract class Parser<T> {
|
|||
///
|
||||
/// If [backtrack] is `true` (default), then a failed parse will not
|
||||
/// 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].
|
||||
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);
|
||||
|
||||
@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.
|
||||
Parser<List<T>> where(bool Function(T) f) =>
|
||||
|
|
|
@ -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());
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
if (!result.successful) return result;
|
||||
|
||||
result = result.change(
|
||||
|
|
|
@ -8,19 +8,18 @@ class _Index<T> extends Parser<T> {
|
|||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
ParseResult<List<T>> result = parser._parse(args.increaseDepth());
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
Object? value;
|
||||
|
||||
if (result.successful) {
|
||||
var vList = result.value;
|
||||
if (vList == null) {
|
||||
throw ArgumentError("ParseResult is null");
|
||||
throw ArgumentError('ParseResult is null');
|
||||
}
|
||||
if (index == -1) {
|
||||
value = vList.last;
|
||||
} else {
|
||||
if (index < vList.length) {
|
||||
//TODO: Look at this
|
||||
// print(">>>>Index: $index, Size: ${vList.length}");
|
||||
// value =
|
||||
// index == -1 ? result.value!.last : result.value!.elementAt(index);
|
||||
|
|
|
@ -25,16 +25,16 @@ class _Longest<T> extends Parser<T> {
|
|||
return ParseResult(args.trampoline, args.scanner, this, false, []);
|
||||
}
|
||||
|
||||
int replay = args.scanner.position;
|
||||
var replay = args.scanner.position;
|
||||
var errors = <SyntaxError>[];
|
||||
var results = <ParseResult<T>>[];
|
||||
|
||||
for (var parser in inactive) {
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
|
||||
if (result.successful && result.span != null)
|
||||
if (result.successful && result.span != null) {
|
||||
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;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ class _Longest<T> extends Parser<T> {
|
|||
return results.first;
|
||||
}
|
||||
|
||||
if (errorMessage != false)
|
||||
if (errorMessage != false) {
|
||||
errors.add(
|
||||
SyntaxError(
|
||||
severity,
|
||||
|
@ -54,22 +54,23 @@ class _Longest<T> extends Parser<T> {
|
|||
args.scanner.emptySpan,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ParseResult(args.trampoline, args.scanner, this, false, errors);
|
||||
}
|
||||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
int replay = args.scanner.position;
|
||||
var replay = args.scanner.position;
|
||||
var errors = <SyntaxError>[];
|
||||
var results = <ParseResult<T>>[];
|
||||
|
||||
for (var parser in parsers) {
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
|
||||
if (result.successful)
|
||||
if (result.successful) {
|
||||
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;
|
||||
}
|
||||
|
@ -97,7 +98,7 @@ class _Longest<T> extends Parser<T> {
|
|||
buffer
|
||||
..writeln('longest(${parsers.length}) (')
|
||||
..indent();
|
||||
int i = 1;
|
||||
var i = 1;
|
||||
|
||||
for (var parser in parsers) {
|
||||
buffer
|
||||
|
|
|
@ -15,7 +15,7 @@ class _Match<T> extends Parser<T> {
|
|||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
var scanner = args.scanner;
|
||||
if (!scanner.scan(pattern))
|
||||
if (!scanner.scan(pattern)) {
|
||||
return ParseResult(args.trampoline, scanner, this, false, [
|
||||
SyntaxError(
|
||||
severity,
|
||||
|
@ -23,6 +23,7 @@ class _Match<T> extends Parser<T> {
|
|||
scanner.emptySpan,
|
||||
),
|
||||
]);
|
||||
}
|
||||
return ParseResult<T>(
|
||||
args.trampoline,
|
||||
scanner,
|
||||
|
|
|
@ -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());
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
|
||||
if (!result.successful) args.scanner.position = replay;
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@ class _Reduce<T> extends Parser<T> {
|
|||
|
||||
@override
|
||||
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>(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
|
@ -18,6 +18,7 @@ class _Reduce<T> extends Parser<T> {
|
|||
false,
|
||||
result.errors,
|
||||
);
|
||||
}
|
||||
|
||||
result = result.change(
|
||||
value: result.value?.isNotEmpty == true ? result.value : []);
|
||||
|
|
|
@ -8,23 +8,26 @@ class Reference<T> extends Parser<T> {
|
|||
|
||||
Reference._();
|
||||
|
||||
void set parser(Parser<T> value) {
|
||||
if (_parser != null)
|
||||
set parser(Parser<T> value) {
|
||||
if (_parser != null) {
|
||||
throw StateError('There is already a parser assigned to this reference.');
|
||||
}
|
||||
_parser = value;
|
||||
}
|
||||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
if (_parser == null)
|
||||
if (_parser == null) {
|
||||
throw StateError('There is no parser assigned to this reference.');
|
||||
}
|
||||
return _parser!._parse(args);
|
||||
}
|
||||
|
||||
@override
|
||||
ParseResult<T> _parse(ParseArgs args) {
|
||||
if (_parser == null)
|
||||
if (_parser == null) {
|
||||
throw StateError('There is no parser assigned to this reference.');
|
||||
}
|
||||
return _parser!._parse(args);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,8 @@ class _Repeat<T> extends ListParser<T> {
|
|||
var errors = <SyntaxError>[];
|
||||
var results = <T>[];
|
||||
var spans = <FileSpan>[];
|
||||
int success = 0, replay = args.scanner.position;
|
||||
var success = 0;
|
||||
var replay = args.scanner.position;
|
||||
ParseResult<T> result;
|
||||
|
||||
do {
|
||||
|
|
|
@ -13,7 +13,7 @@ class _ToList<T> extends ListParser<T> {
|
|||
return (result as ParseResult<List<T>>).change(parser: this);
|
||||
}
|
||||
|
||||
List<T> values = [];
|
||||
var values = <T>[];
|
||||
if (result.value != null) {
|
||||
values.add(result.value!);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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.
|
||||
homepage: https://github.com/dukefirehawk/angel/tree/angel3/packages/combinator
|
||||
environment:
|
||||
|
@ -11,4 +11,5 @@ dependencies:
|
|||
string_scanner: ^1.1.0
|
||||
tuple: ^2.0.0
|
||||
dev_dependencies:
|
||||
test: ^1.17.4
|
||||
test: ^1.17.4
|
||||
pedantic: ^1.11.0
|
||||
|
|
|
@ -2,7 +2,7 @@ import 'package:angel3_combinator/angel3_combinator.dart';
|
|||
import 'package:test/test.dart';
|
||||
import 'common.dart';
|
||||
|
||||
main() {
|
||||
void main() {
|
||||
var number = chain([
|
||||
match(RegExp(r'[0-9]+')).value((r) => int.parse(r.span!.text)),
|
||||
match(',').opt(),
|
||||
|
|
|
@ -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() {}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue