diff --git a/.gitignore b/.gitignore index 155a051..eaa784c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ doc/gen doc/tutorial/bin/packages .idea/ .packages +.dart_tool diff --git a/CHANGELOG.md b/CHANGELOG.md index aaaf2f5..9b55235 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Parsers Changelog +## 2.0.0 + +- Dart 2.0 compatible +- Operators disabled for now, so instead of `parser1 | parser2` use `parser1.or(parser2)` +- `%` operator is an exception as it does not require any type parameters +- Examples in `example` directory still don't work. + ## 1.0.0 - Make strong mode and dart 2 compatible. diff --git a/analysis_options.yaml b/analysis_options.yaml index 518eb90..c815adf 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,2 +1,34 @@ analyzer: - strong-mode: true \ No newline at end of file + strong-mode: + implicit-casts: false + implicit-dynamic: true + +linter: + rules: + # - avoid_positional_boolean_parameters + - avoid_types_on_closure_parameters + - avoid_unused_constructor_parameters + - directives_ordering + - file_names + # - flutter_style_todos + - parameter_assignments + - prefer_adjacent_string_concatenation + - prefer_asserts_in_initializer_lists + - prefer_const_declarations + - prefer_final_fields + - prefer_final_locals + # - prefer_for_elements_to_map_fromIterable # not yet landed in stable + - prefer_generic_function_type_aliases + # - prefer_if_elements_to_conditional_expressions # not yet landed in stable + - prefer_is_empty + - prefer_single_quotes + # - prefer_spread_collections # not yet landed in stable + - prefer_typing_uninitialized_variables + - recursive_getters + - unnecessary_await_in_return + - unnecessary_lambdas + - unnecessary_const + - unnecessary_new + - use_function_type_syntax_for_parameters + - use_rethrow_when_possible + - use_to_and_as_if_applicable diff --git a/example/full_arith.dart b/example/full_arith.dart index a99e43f..ce31ccd 100644 --- a/example/full_arith.dart +++ b/example/full_arith.dart @@ -1,7 +1,9 @@ // Copyright (c) 2012, Google Inc. All rights reserved. Use of this source code // is governed by a BSD-style license that can be found in the LICENSE file. -// Author: Paul Brauner (polux@google.com) +// Authors: +// Paul Brauner (polux@google.com) +// Dinesh Ahuja (dev@kabiir.me) library example; @@ -10,35 +12,37 @@ import 'package:parsers/parsers.dart'; // Same example as example.dart, with the additional use of chainl1 which // helps handling infix operators with the same precedence. -class Arith { +typedef MathFunction = T Function(T a, T b); - digits2int(digits) => int.parse(digits.join()); +class Arith { + int digits2int(List digits) => int.parse(digits.join()); - lexeme(parser) => parser < spaces; - token(str) => lexeme(string(str)); - parens(parser) => parser.between(token('('), token(')')); + Parser lexeme(Parser parser) => parser.thenDrop(spaces); + Parser token(String str) => lexeme(string(str)); + Parser parens(Parser parser) => + parser.between(token('('), token(')')); - get start => expr() < eof; + Parser get start => expr().thenDrop(eof); - get comma => token(','); - get times => token('*'); - get div => token('~/'); - get plus => token('+'); - get minus => token('-'); - get number => lexeme(digit.many1) ^ digits2int; + Parser get comma => token(','); + Parser get times => token('*'); + Parser get div => token('~/'); + Parser get plus => token('+'); + Parser get minus => token('-'); + Parser get number => (lexeme(digit.many1).map(digits2int)); - expr() => rec(term).chainl1(addop); - term() => rec(atom).chainl1(mulop); - atom() => number | parens(rec(expr)); + Parser expr() => rec(term).chainl1(addop); + Parser term() => rec(atom).chainl1(mulop); + Parser atom() => number.or(parens(rec(expr))); - get addop => (plus > success((x, y) => x + y)) - | (minus > success((x, y) => x - y)); + Parser get addop => (plus.thenKeep(success((x, y) => x + y))) + .or(minus.thenKeep(success((x, y) => x - y))); - get mulop => (times > success((x, y) => x * y)) - | (div > success((x, y) => x ~/ y)); + Parser get mulop => (times.thenKeep(success((x, y) => x * y))) + .or(div.thenKeep(success((x, y) => x ~/ y))); } main() { - final s = "1 * 2 ~/ 2 + 3 * (4 + 5 - 1)"; - print(new Arith().start.parse(s)); // prints 25 + const s = '1 * 2 ~/ 2 + 3 * (4 + 5 - 1)'; + print(Arith().start.parse(s)); // prints 25 } diff --git a/example/mini_ast.dart b/example/mini_ast.dart index 4c2ee3c..ea0532f 100644 --- a/example/mini_ast.dart +++ b/example/mini_ast.dart @@ -82,107 +82,101 @@ class FieldDeclaration { } NamespaceDeclaration namespaceDeclarationMapping(doc, _, name, body, __) => - new NamespaceDeclaration(name, body, doc); + NamespaceDeclaration(name, body, doc); InterfaceDeclaration interfaceDeclarationMapping(doc, _, name, body, __) => - new InterfaceDeclaration(name, body, doc); + InterfaceDeclaration(name, body, doc); MethodDeclaration methodDeclarationRegularMapping( - doc, returnType, name, parameters, _) => - new MethodDeclaration(returnType, name, parameters, doc); + doc, returnType, name, parameters, _) => + MethodDeclaration(returnType, name, parameters, doc); MethodDeclaration methodDeclarationReservedMapping( - doc, returnType, name, parameters, _) => - new MethodDeclaration(new TypeAppl(returnType, null), name, parameters, doc); + doc, returnType, name, parameters, _) => + MethodDeclaration(TypeAppl(returnType, null), name, parameters, doc); -DictionaryDeclaration dictionaryDeclarationMapping( - doc, _, name, body, __) => - new DictionaryDeclaration(name, body, doc); +DictionaryDeclaration dictionaryDeclarationMapping(doc, _, name, body, __) => + DictionaryDeclaration(name, body, doc); FieldDeclaration fieldDeclarationMapping(doc, type, name, _) => - new FieldDeclaration(type, name, doc); + FieldDeclaration(type, name, doc); class DataCoreParser extends LanguageParsers { - - DataCoreParser() : super(reservedNames: reservedNames, - // tells LanguageParsers to not handle comments - commentStart: "", - commentEnd: "", - commentLine: ""); + DataCoreParser() + : super( + reservedNames: reservedNames, + // tells LanguageParsers to not handle comments + commentStart: "", + commentEnd: "", + commentLine: ""); Parser get docString => lexeme(_docString).many; Parser get _docString => - everythingBetween(string('//'), string('\n')) - | everythingBetween(string('/*'), string('*/')) - | everythingBetween(string('/**'), string('*/')); + everythingBetween(string('//'), string('\n')) | + everythingBetween(string('/*'), string('*/')) | + everythingBetween(string('/**'), string('*/')); Parser get namespaceDeclaration => - docString - + reserved["namespace"] - + identifier - + braces(namespaceBody) - + semi - ^ namespaceDeclarationMapping; + docString + + reserved["namespace"] + + identifier + + braces(namespaceBody) + + semi ^ + namespaceDeclarationMapping; Parser get namespaceBody => body.many; Parser get body => interfaceDeclaration | dictionaryDeclaration; Parser get interfaceDeclaration => - docString - + reserved["interface"] - + identifier - + braces(interfaceBody) - + semi - ^ interfaceDeclarationMapping; + docString + + reserved["interface"] + + identifier + + braces(interfaceBody) + + semi ^ + interfaceDeclarationMapping; Parser get interfaceBody => method.many; Parser get method => regularMethod | voidMethod; Parser typeAppl() => - identifier - + angles(rec(typeAppl).sepBy(comma)).orElse([]) - ^ (c, args) => new TypeAppl(c, args); + identifier + angles(rec(typeAppl).sepBy(comma)).orElse([]) ^ + (c, args) => TypeAppl(c, args); Parser get parameter => - (typeAppl() % 'type') - + (identifier % 'parameter') - ^ (t, p) => new Parameter(t, p); + (typeAppl() % 'type') + (identifier % 'parameter') ^ + (t, p) => Parameter(t, p); Parser get regularMethod => - docString - + typeAppl() - + identifier - + parens(parameter.sepBy(comma)) - + semi - ^ methodDeclarationRegularMapping; + docString + + typeAppl() + + identifier + + parens(parameter.sepBy(comma)) + + semi ^ + methodDeclarationRegularMapping; Parser get voidMethod => - docString - + reserved['void'] - + identifier - + parens(parameter.sepBy(comma)) - + semi - ^ methodDeclarationReservedMapping; + docString + + reserved['void'] + + identifier + + parens(parameter.sepBy(comma)) + + semi ^ + methodDeclarationReservedMapping; Parser get dictionaryDeclaration => - docString - + reserved["dictionary"] - + identifier - + braces(dictionaryBody) - + semi - ^ dictionaryDeclarationMapping; + docString + + reserved["dictionary"] + + identifier + + braces(dictionaryBody) + + semi ^ + dictionaryDeclarationMapping; Parser get dictionaryBody => field.many; Parser get field => - docString - + typeAppl() - + identifier - + semi - ^ fieldDeclarationMapping; + docString + typeAppl() + identifier + semi ^ fieldDeclarationMapping; } final test = """ @@ -219,7 +213,7 @@ namespace datacore { """; void main() { - DataCoreParser dataCoreParser = new DataCoreParser(); + DataCoreParser dataCoreParser = DataCoreParser(); NamespaceDeclaration namespaceDeclaration = dataCoreParser.namespaceDeclaration.between(spaces, eof).parse(test); print(namespaceDeclaration); diff --git a/example/mini_lang.dart b/example/mini_lang.dart index 54b8622..daf2725 100644 --- a/example/mini_lang.dart +++ b/example/mini_lang.dart @@ -16,18 +16,17 @@ class MiniLang extends LanguageParsers { get start => stmts().between(spaces, eof); stmts() => stmt().endBy(semi); - stmt() => declStmt() - | assignStmt() - | ifStmt(); + stmt() => declStmt() | assignStmt() | ifStmt(); // In a real parser, we would build AST nodes, but here we turn sequences // into lists via the list getter for simplicity. declStmt() => (reserved['var'] + identifier + symbol('=') + expr()).list; assignStmt() => (identifier + symbol('=') + expr()).list; - ifStmt() => (reserved['if'] - + parens(expr()) - + braces(rec(stmts)) - + reserved['else'] - + braces(rec(stmts))).list; + ifStmt() => (reserved['if'] + + parens(expr()) + + braces(rec(stmts)) + + reserved['else'] + + braces(rec(stmts))) + .list; expr() => disj().sepBy1(symbol('||')); disj() => comp().sepBy1(symbol('&&')); @@ -35,13 +34,14 @@ class MiniLang extends LanguageParsers { arith() => term().sepBy1(symbol('+') | symbol('-')); term() => atom().withPosition.sepBy1(symbol('*') | symbol('/')); - atom() => floatLiteral - | intLiteral - | stringLiteral - | reserved['true'] - | reserved['false'] - | identifier - | parens(rec(expr)); + atom() => + floatLiteral | + intLiteral | + stringLiteral | + reserved['true'] | + reserved['false'] | + identifier | + parens(rec(expr)); } final test = """ @@ -59,5 +59,5 @@ final test = """ """; main() { - print(new MiniLang().start.parse(test)); + print(MiniLang().start.parse(test)); } diff --git a/example/simple_arith.dart b/example/simple_arith.dart index b3d400e..d5ed502 100644 --- a/example/simple_arith.dart +++ b/example/simple_arith.dart @@ -8,12 +8,11 @@ library simple_arith; import 'package:parsers/parsers.dart'; class Arith { - // Some combinators: functions that take parsers and return a parser. lexeme(parser) => parser < spaces; - token(str) => lexeme(string(str)); - parens(parser) => token('(') + parser + token(')') ^ (a,b,c) => b; + token(str) => lexeme(string(str as String)); + parens(parser) => token('(') + parser + token(')') ^ (a, b, c) => b; // The axiom of the grammar is an expression followed by end of file. @@ -21,34 +20,33 @@ class Arith { // We define some lexemes. - get comma => token(','); - get times => token('*'); - get plus => token('+'); + get comma => token(','); + get times => token('*'); + get plus => token('+'); get number => (lexeme(digit.many1) ^ digits2int) % 'natural number'; // This is the gist of the grammar, the BNF-like rules. - expr() => rec(mult).sepBy1(plus) ^ sum; - mult() => rec(atom).sepBy1(times) ^ prod; - atom() => number - | parens(rec(expr)); + expr() => rec(mult as Parser Function()).sepBy1(plus as Parser) ^ sum; + mult() => rec(atom as Parser Function()).sepBy1(times as Parser) ^ prod; + atom() => number | parens(rec(expr as Parser Function())); // These are simple Dart functions used as "actions" above to transform the // results of intermediate parsing steps. - digits2int(digits) => int.parse(digits.join()); - prod(xs) => xs.fold(1, (a,b) => a * b); - sum(xs) => xs.fold(0, (a,b) => a + b); + digits2int(digits) => int.parse((digits as Iterable).join()); + prod(xs) => xs.fold(1, (a, b) => a * b); + sum(xs) => xs.fold(0, (a, b) => a + b); } main() { final good = "1 * 2 + 3 * (4 + 5)"; - print(new Arith().start.parse(good)); // prints 29 + print(Arith().start.parse(good)); // prints 29 final bad = "1 * x + 2"; try { - new Arith().start.parse(bad); - } catch(e) { + Arith().start.parse(bad); + } catch (e) { print('parsing of 1 * x + 2 failed as expected: "$e"'); } } diff --git a/lib/option.dart b/lib/option.dart new file mode 100644 index 0000000..3e7ab07 --- /dev/null +++ b/lib/option.dart @@ -0,0 +1,47 @@ +class Option { + final T _value; + final bool isDefined; + + const Option._internal(this.isDefined, this._value); + + factory Option.none() => const Option._internal(false, null); + + factory Option.some(T value) => Option._internal(true, value); + + factory Option.fromNullable(T nullableValue) => + nullableValue == null ? Option.none() : Option.some(nullableValue); + + T get value { + if (isDefined) return _value; + throw StateError('Option.none() has no value'); + } + + T get asNullable => isDefined ? _value : null; + + T orElse(T defaultValue) => isDefined ? _value : defaultValue; + + T orElseCompute(T Function() defaultValue) => + isDefined ? _value : defaultValue(); + + // /// [:forall U, Option map(U f(T value)):] + // Option map(Function(T value) f) => isDefined ? Option.some(f(_value)) : this; + + /// [:forall U, Option map(Option f(T value)):] + Option expand(Option Function(T value) f) => isDefined ? f(_value) : this; + + /// Precondition: [:this is Option { final A value; final Position position; @@ -66,13 +70,12 @@ class PointedValue { abstract class Expectations { const Expectations(); - factory Expectations.empty(Position position) => - new EmptyExpectation(position); + factory Expectations.empty(Position position) => EmptyExpectation(position); factory Expectations.single(String str, Position position) => - new SingleExpectation(str, position); + SingleExpectation(str, position); CombinedExpectation best(Expectations other) => - new CombinedExpectation(this, other); + CombinedExpectation(this, other); Position get position; Set get expected; @@ -84,7 +87,7 @@ class EmptyExpectation extends Expectations { const EmptyExpectation(this._position); Position get position => _position; - Set get expected => new Set(); + Set get expected => Set(); } class SingleExpectation extends Expectations { @@ -94,7 +97,7 @@ class SingleExpectation extends Expectations { SingleExpectation(this._expected, this._position); Position get position => _position; - Set get expected => new Set.from([_expected]); + Set get expected => Set.from([_expected]); } class CombinedExpectation extends Expectations { @@ -124,40 +127,38 @@ class ParseResult { ParseResult(this.text, this.expectations, this.position, this.isSuccess, this.isCommitted, this.value); - factory ParseResult.success(A value, String text, Position position, + static ParseResult success(A value, String text, Position position, [Expectations expectations, bool committed = false]) { - final Expectations exps = (expectations != null) - ? expectations - : new Expectations.empty(position); - return new ParseResult(text, exps, position, true, committed, value); + return ParseResult(text, expectations ?? Expectations.empty(position), + position, true, committed ?? false, value); } - factory ParseResult.failure(String text, Position position, - [Expectations expectations, bool committed = false]) { - final Expectations exps = (expectations != null) - ? expectations - : new Expectations.empty(position); - return new ParseResult(text, exps, position, false, committed, null); + static ParseResult failure(String text, Position position, + [Expectations expectations, bool committed = false, A value = null]) { + return ParseResult(text, expectations ?? Expectations.empty(position), + position, false, committed ?? false, value); } - ParseResult map(B f(A value)) { + ParseResult map(B Function(A value) f) { return copy(value: f(value)); } - ParseResult copy( - {String text, - Expectations expectations, - Position position, - bool isSuccess, - bool isCommitted, - Object value: const Undefined()}) { - return new ParseResult( - (text != null) ? text : this.text, - (expectations != null) ? expectations : this.expectations, - (position != null) ? position : this.position, - (isSuccess != null) ? isSuccess : this.isSuccess, - (isCommitted != null) ? isCommitted : this.isCommitted, - (value != const Undefined()) ? value : this.value); + ParseResult copy({ + String text, + Expectations expectations, + Position position, + bool isSuccess, + bool isCommitted, + B value, + }) { + return ParseResult( + text ?? this.text, + expectations ?? this.expectations, + position ?? this.position, + isSuccess ?? this.isSuccess, + isCommitted ?? this.isCommitted, + cast(value ?? this.value), + ); } String get errorMessage { @@ -170,7 +171,7 @@ class ParseResult { return '$prelude unexpected $maxSeenChar.'; } else { final or = _humanOr(expected.toList()); - return "$prelude expected $or, got $maxSeenChar."; + return '$prelude expected $or, got $maxSeenChar.'; } } @@ -186,11 +187,11 @@ class ParseResult { } static String _humanOr(List es) { - assert(es.length > 0); + assert(es?.isNotEmpty ?? false); if (es.length == 1) { - return es[0]; + return es[0] as String; } else { - StringBuffer result = new StringBuffer(); + final result = StringBuffer(); for (int i = 0; i < es.length - 2; i++) { result.write('${es[i]}, '); } @@ -200,45 +201,45 @@ class ParseResult { } } -typedef ParseResult _ParseFunction(String s, Position pos); - class Parser { - final _ParseFunction _run; + /// Consume [s] at [pos] and return result of type [A] + final ParseResult Function(String s, Position pos) _run; - Parser(ParseResult f(String s, Position pos)) : this._run = f; + Parser(this._run); + /// Consume [s] at [pos] and return result of type [A] ParseResult run(String s, [Position pos = const Position(0, 1, 1)]) => _run(s, pos); - A parse(String s, {int tabStop: 1}) { - ParseResult result = run(s, new Position(0, 1, 1, tabStop: tabStop)); + A parse(String s, {int tabStop = 1}) { + final result = run(s, Position(0, 1, 1, tabStop: tabStop)); if (result.isSuccess) return result.value; else throw result.errorMessage; } - Parser then(Parser g(A value)) { - return new Parser((text, pos) { - ParseResult res = _run(text, pos); + Parser then(Parser Function(A value) g) { + return Parser((text, pos) { + final res = run(text, pos); if (res.isSuccess) { - final res2 = g(res.value)._run(text, res.position); + 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; + return res.copy(value: cast(res.value)); } }); } /// Alias for [then]. - Parser operator >>(Parser g(A x)) => then(g); + Parser operator >>(Parser Function(A x) g) => then(g); Parser expecting(String expected) { - return new Parser((s, pos) { - final res = _run(s, pos); - return res.copy(expectations: new Expectations.single(expected, pos)); + return Parser((s, pos) { + final res = run(s, pos); + return res.copy(expectations: Expectations.single(expected, pos)); }); } @@ -246,16 +247,16 @@ class Parser { Parser operator %(String expected) => this.expecting(expected); Parser get committed { - return new Parser((s, pos) { - final res = _run(s, pos); + return Parser((s, pos) { + final res = run(s, pos); return res.copy(isCommitted: true); }); } - // Assumes that [this] parses a function (i.e., A = B -> C) and applies it - // to the result of [p]. + /// Assumes that [this] parses a function (i.e., A = B -> C) and applies it + /// to the result of [p]. Parser apply(Parser p) => - then((f) => p.then((x) => success((f as Function)(x)))); + this.then((f) => p.then((x) => success((f as Function)(x) as C))); /// Alias for [apply]. Parser operator *(Parser p) => apply(p); @@ -274,103 +275,96 @@ class Parser { Parser operator <(Parser p) => thenDrop(p); /// Maps [f] over the result of [this]. - Parser map(B f(A x)) => success(f) * this; + Parser map(B Function(A x) f) => (success(f).apply(this)); /// Alias for [map]. - Parser operator ^(Object f(A x)) => map(f); + Parser operator ^(Object Function(A x) f) => map(f); /// Parser sequencing: creates a parser accumulator. - ParserAccumulator2 and(Parser p) => - new ParserAccumulator2(this, p); + ParserAccumulator2 and(Parser p) => ParserAccumulator2(this, p); /// Alias for [and]. - ParserAccumulator2 operator +(Parser p) => new ParserAccumulator2(this, p); + ParserAccumulator2 operator +(Parser p) => ParserAccumulator2(this, p); /// Alternative. - Parser or(Parser p) { - return new Parser((s, pos) { - ParseResult res = _run(s, pos); + // refact(devkabiir): Since B extends A, A can always be used in place of B, not the other way around + // probably don't even need type parameters? + Parser or(Parser p) { + ParseResult parserFunction(String s, Position pos) { + final res = run(s, pos); if (res.isSuccess || res.isCommitted) { - return res; + return res.copy(value: cast(res.value)); } else { - ParseResult res2 = p._run(s, pos); - return res2.copy( + final res2 = p.run(s, pos); + return res2.copy( expectations: res.expectations.best(res2.expectations)); } - }); + } + + return Parser(parserFunction); } /// Alias for [or]. - Parser operator |(Parser p) => or(p); + Parser operator |(Parser p) => or(p as Parser); - /** - * Parses without consuming any input. - * - * Used for defining followedBy, which is probably what you're looking for. - */ + /// Parses without consuming any input. + /// + /// Used for defining followedBy, which is probably what you're looking for. Parser get lookAhead { - return new Parser((s, pos) { - ParseResult res = _run(s, pos); - return res.isSuccess ? new ParseResult.success(res.value, s, pos) : res; + return Parser((s, pos) { + final res = run(s, pos); + return (res.isSuccess ? ParseResult.success(res.value, s, pos) : res); }); } - /** - * Succeeds if and only if [this] succeeds and [p] succeeds on what remains to - * parse without consuming it. - * - * string("let").followedBy(space) - */ + /// Succeeds if and only if [this] succeeds and [p] succeeds on what remains to + /// parse without consuming it. + /// + /// string("let").followedBy(space) Parser followedBy(Parser p) => thenDrop(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. - */ + /// 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 Parser((s, pos) { + final res = run(s, pos); return res.isSuccess - ? new ParseResult.failure(s, pos) - : new ParseResult.success(null, s, pos); + ? ParseResult.failure(s, pos) + : ParseResult.success(null, s, pos); }); } - /** - * Succeeds if and only if [this] succeeds and [p] fails on what remains to - * parse. - * - * string("let").notFollowedBy(alphanum) - */ + /// Succeeds if and only if [this] succeeds and [p] fails on what remains to + /// parse. + /// + /// string("let").notFollowedBy(alphanum) Parser notFollowedBy(Parser p) => thenDrop(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. - */ + /// 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> manyUntil(Parser end) { // Imperative version to avoid stack overflows. - return new Parser((s, pos) { - List res = []; + return Parser((s, pos) { + final List res = []; Position index = pos; - var exps = new Expectations.empty(pos); + var exps = Expectations.empty(pos); bool committed = false; while (true) { - final endRes = end._run(s, index); + 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); + final xRes = this.run(s, index); exps = exps.best(xRes.expectations); committed = committed || xRes.isCommitted; if (xRes.isSuccess) { @@ -386,27 +380,25 @@ class Parser { }); } - /** - * 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. - */ + /// 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) { + return Parser((s, pos) { Position index = pos; - var exps = new Expectations.empty(pos); + var exps = Expectations.empty(pos); var commit = false; while (true) { - final endRes = end._run(s, index); + 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); + final xRes = this.run(s, index); exps = exps.best(xRes.expectations); commit = commit || xRes.isCommitted; if (xRes.isSuccess) { @@ -426,17 +418,17 @@ class Parser { Parser orElse(A value) => or(success(value)); Parser> get maybe => - this.map((x) => new Option.some(x)).orElse(new Option.none()); + this.map((x) => Option.some(x)).orElse(Option.none()); // Imperative version to avoid stack overflows. - Parser> _many(List acc()) { - return new Parser((s, pos) { + Parser> _many(List Function() acc) { + return Parser((s, pos) { final res = acc(); - var exps = new Expectations.empty(pos); + var exps = Expectations.empty(pos); Position index = pos; bool committed = false; while (true) { - ParseResult o = this._run(s, index); + final o = this.run(s, index); exps = exps.best(o.expectations); committed = committed || o.isCommitted; if (o.isSuccess) { @@ -445,7 +437,7 @@ class Parser { } else if (o.isCommitted) { return o.copy(expectations: exps); } else { - return new ParseResult.success(res, s, index, exps, committed); + return ParseResult.success(res, s, index, exps, committed); } } }); @@ -453,21 +445,19 @@ class Parser { Parser> get many => _many(() => []); - Parser> get many1 => then((A x) => _many(() => [x])); + Parser> get many1 => then((x) => _many(() => [x])); - /** - * Parses [this] zero or more time, skipping its result. - * - * Equivalent to [this.many > success(null)] but more efficient. - */ + /// 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) { + return Parser((s, pos) { Position index = pos; - var exps = new Expectations.empty(pos); + var exps = Expectations.empty(pos); bool committed = false; while (true) { - ParseResult o = this._run(s, index); + final o = this.run(s, index); exps = exps.best(o.expectations); committed = committed || o.isCommitted; if (o.isSuccess) { @@ -475,17 +465,15 @@ class Parser { } else if (o.isCommitted) { return o.copy(expectations: exps); } else { - return new ParseResult.success(null, s, index, exps, committed); + return ParseResult.success(null, s, index, exps, committed); } } }); } - /** - * Parses [this] one or more time, skipping its result. - * - * Equivalent to [this.many1 > success(null)] but more efficient. - */ + /// Parses [this] one or more time, skipping its result. + /// + /// Equivalent to [this.many1 > success(null)] but more efficient. Parser get skipMany1 => thenKeep(this.skipMany); Parser> sepBy(Parser sep) => sepBy1(sep).orElse([]); @@ -497,16 +485,12 @@ class Parser { Parser> endBy1(Parser sep) => thenDrop(sep).many1; - /** - * Parses zero or more occurences of [this] separated and optionally ended - * by [sep]. - */ + /// Parses zero or more occurences of [this] separated and optionally ended + /// by [sep]. Parser> sepEndBy(Parser sep) => sepEndBy1(sep).orElse([]); - /** - * Parses one or more occurences of [this] separated and optionally ended - * by [sep]. - */ + /// Parses one or more occurences of [this] separated and optionally ended + /// by [sep]. Parser> sepEndBy1(Parser sep) => sepBy1(sep).thenDrop(sep.maybe); @@ -515,22 +499,24 @@ class Parser { Parser chainl1(Parser sep) { Parser rest(A acc) { - return new Parser((s, pos) { + return Parser((s, pos) { Position index = pos; - var exps = new Expectations.empty(pos); + var exps = Expectations.empty(pos); var commit = false; while (true) { - combine(Function f) => (A x) => f(acc, x); - final res = success(combine).apply(sep).apply(this)._run(s, index); + // ignore: avoid_types_on_closure_parameters + combine(Function(A, A) f) => (A x) => f(acc, x); + final res = success(combine).apply(sep).apply(this).run(s, index); exps = exps.best(res.expectations); commit = commit || res.isCommitted; if (res.isSuccess) { - acc = res.value; + // ignore: parameter_assignments + acc = cast(res.value); index = res.position; } else if (res.isCommitted) { return res.copy(expectations: exps); } else { - return new ParseResult.success(acc, s, index, exps, commit); + return ParseResult.success(acc, s, index, exps, commit); } } }); @@ -545,7 +531,8 @@ class Parser { /// Warning: may lead to stack overflows. Parser chainr1(Parser sep) { - Parser rest(A x) => success((Function f) => (A y) => f(x, y)) + // ignore: avoid_types_on_closure_parameters + Parser rest(A x) => success((Function(A, A) f) => (A y) => f(x, y)) .apply(sep) .apply(chainr1(sep)) .or(success(x)); @@ -557,7 +544,7 @@ class Parser { /// Returns the substring consumed by [this]. Parser get record { - return new Parser((s, pos) { + return Parser((s, pos) { final result = run(s, pos); if (result.isSuccess) { return result.copy( @@ -568,46 +555,49 @@ class Parser { }); } - /** - * Returns the value parsed by [this] along with the position at which it - * has been parsed. - */ + /// Returns the value parsed by [this] along with the position at which it + /// has been parsed. Parser> get withPosition { - return new Parser((s, pos) { - return this.map((v) => new PointedValue(v, pos))._run(s, pos); + return Parser((s, pos) { + return (this.map((v) => PointedValue(v, pos)).run(s, pos)); }); } } // Primitive parsers -final Parser fail = new Parser((s, pos) => new ParseResult.failure(s, pos)); +// final Parser fail = Parser((s, pos) => ParseResult.failure(s, pos)); + +/// Create a parser that fails with [value]. +/// Prefer specifying a value to help type system +Parser failure([A value = null]) => + Parser((s, pos) => ParseResult.failure(s, pos, null, null, value)); Parser success(A value) => - new Parser((s, pos) => new ParseResult.success(value, s, pos)); + Parser((s, pos) => ParseResult.success(value, s, pos)); -final Parser eof = new Parser((s, pos) => pos.offset >= s.length - ? new ParseResult.success(null, s, pos) - : new ParseResult.failure(s, pos, new Expectations.single("eof", pos))); +final Parser eof = Parser((s, pos) => pos.offset >= s.length + ? ParseResult.success(null, s, pos) + : ParseResult.failure(s, pos, Expectations.single('eof', pos))); -Parser pred(bool p(String char)) { - return new Parser((s, pos) { +Parser pred(bool Function(String char) p) { + return Parser((s, pos) { if (pos.offset >= s.length) - return new ParseResult.failure(s, pos); + return ParseResult.failure(s, pos); else { - String c = s[pos.offset]; + final c = s[pos.offset]; return p(c) - ? new ParseResult.success(c, s, pos.addChar(c)) - : new ParseResult.failure(s, pos); + ? ParseResult.success(c, s, pos.addChar(c)) + : ParseResult.failure(s, pos); } }); } -Parser char(String chr) => pred((String c) => c == chr) % "'$chr'"; +Parser char(String chr) => pred((c) => c == chr) % "'$chr'"; Parser string(String str) { // Primitive version for efficiency - return new Parser((s, pos) { + return Parser((s, pos) { final int offset = pos.offset; final int max = offset + str.length; @@ -627,28 +617,29 @@ Parser string(String str) { update(c); } if (match) { - return new ParseResult.success( + return ParseResult.success( str, s, pos.copy(offset: max, line: newline, character: newchar)); } else { - return new ParseResult.failure( - s, pos, new Expectations.single("'$str'", pos)); + return ParseResult.failure( + s, pos, Expectations.single("'$str'", pos)); } }); } -Parser rec(Parser f()) => new Parser((s, pos) => f()._run(s, pos)); +Parser rec(Parser Function() f) => + Parser((s, pos) => f().run(s, pos)); final Parser position = - new Parser((s, pos) => new ParseResult.success(pos, s, pos)); + Parser((s, pos) => ParseResult.success(pos, s, pos)); // Derived combinators Parser choice(List> ps) { // Imperative version for efficiency - return new Parser((s, pos) { - var exps = new Expectations.empty(pos); + return Parser((s, pos) { + var exps = Expectations.empty(pos); for (final p in ps) { - final res = p._run(s, pos); + final res = p.run(s, pos); exps = exps.best(res.expectations); if (res.isSuccess) { return res.copy(expectations: exps); @@ -656,7 +647,7 @@ Parser choice(List> ps) { return res; } } - return new ParseResult.failure(s, pos, exps); + return ParseResult.failure(s, pos, exps); }); } @@ -670,21 +661,22 @@ class _SkipInBetween { Parser parser() => nested ? _insideMulti() : _insideSingle(); Parser _inside() => rec(parser).between(left, right); - Parser get _leftOrRightAhead => (left | right).lookAhead; - Parser _insideMulti() => anyChar.skipManyUntil(_leftOrRightAhead) > _nest(); - Parser _nest() => (rec(_inside) > rec(_insideMulti)).maybe; + Parser get _leftOrRightAhead => (left.or(right)).lookAhead; + Parser _insideMulti() => + anyChar.skipManyUntil(_leftOrRightAhead).thenKeep(_nest()); + Parser _nest() => (rec(_inside).thenKeep(rec(_insideMulti))).maybe; Parser _insideSingle() => anyChar.skipManyUntil(right.lookAhead); } Parser skipEverythingBetween(Parser left, Parser right, - {bool nested: false}) { - final inBetween = new _SkipInBetween(left, right, nested).parser(); - return inBetween.between(left, right) > success(null); + {bool nested = false}) { + final inBetween = _SkipInBetween(left, right, nested).parser(); + return (inBetween.between(left, right).thenKeep(success(null))); } Parser everythingBetween(Parser left, Parser right, - {bool nested: false}) { - final inBetween = new _SkipInBetween(left, right, nested).parser(); + {bool nested = false}) { + final inBetween = _SkipInBetween(left, right, nested).parser(); return inBetween.record.between(left, right); } @@ -698,12 +690,12 @@ Parser oneOf(String chars) => Parser noneOf(String chars) => pred((c) => !chars.contains(c)).expecting("none of '$chars'"); -final _spaces = " \t\n\r\v\f"; -final _lower = "abcdefghijklmnopqrstuvwxyz"; +const _spaces = ' \t\n\r\v\f'; +const _lower = 'abcdefghijklmnopqrstuvwxyz'; final _upper = _lower.toUpperCase(); -final _alpha = "$_lower$_upper"; -final _digit = "1234567890"; -final _alphanum = "$_alpha$_digit"; +final _alpha = '$_lower$_upper'; +const _digit = '1234567890'; +final _alphanum = '$_alpha$_digit'; final Parser tab = char('\t') % 'tab'; @@ -711,7 +703,7 @@ final Parser newline = char('\n') % 'newline'; final Parser space = oneOf(_spaces) % 'space'; -final Parser spaces = (space.many > success(null)) % 'spaces'; +final Parser spaces = (space.many.thenKeep(success(null))) % 'spaces'; final Parser upper = oneOf(_upper) % 'uppercase letter'; @@ -729,7 +721,7 @@ class ReservedNames { Parser operator [](String key) { final res = _map[key]; if (res == null) - throw "$key is not a reserved name"; + throw '$key is not a reserved name'; else return res; } @@ -748,23 +740,23 @@ class LanguageParsers { ReservedNames _reserved; LanguageParsers( - {String commentStart: '/*', - String commentEnd: '*/', - String commentLine: '//', - bool nestedComments: false, - Parser identStart: null, // letter | char('_') - Parser identLetter: null, // alphanum | char('_') - List reservedNames: const []}) { - final identStartDefault = letter | char('_'); - final identLetterDefault = alphanum | char('_'); + {String commentStart = '/*', + String commentEnd = '*/', + String commentLine = '//', + bool nestedComments = false, + Parser identStart = null, // letter | char('_') + Parser identLetter = null, // alphanum | char('_') + List reservedNames = const []}) { + final identStartDefault = letter.or(char('_')); + final identLetterDefault = alphanum.or(char('_')); _commentStart = commentStart; _commentEnd = commentEnd; _commentLine = commentLine; _nestedComments = nestedComments; - _identStart = (identStart == null) ? identStartDefault : identStart; - _identLetter = (identLetter == null) ? identLetterDefault : identLetter; - _reservedNames = new Set.from(reservedNames); + _identStart = ((identStart == null) ? identStartDefault : identStart); + _identLetter = ((identLetter == null) ? identLetterDefault : identLetter); + _reservedNames = Set.from(reservedNames); } Parser get semi => symbol(';') % 'semicolon'; @@ -772,89 +764,94 @@ class LanguageParsers { Parser get colon => symbol(':') % 'colon'; Parser get dot => symbol('.') % 'dot'; - Parser get _ident => - success((String c) => (List cs) => "$c${cs.join()}") - .apply(_identStart) - .apply(_identLetter.many); + Parser get _ident => success((c) => (cs) => '$c${cs.join()}') + .apply(_identStart) + .apply(_identLetter.many); Parser get identifier => - lexeme(_ident.then( - (name) => _reservedNames.contains(name) ? fail : success(name))) % + lexeme( + _ident.then( + (name) => + _reservedNames.contains(name) ? failure() : success(name), + ), + ) % 'identifier'; ReservedNames get reserved { if (_reserved == null) { - final map = new Map>(); + final map = Map>(); for (final name in _reservedNames) { map[name] = lexeme(string(name).notFollowedBy(_identLetter)); } - _reserved = new ReservedNames._(map); + _reserved = ReservedNames._(map); } return _reserved; } - final Parser _escapeCode = (char('a') > success('\a')) | - (char('b') > success('\b')) | - (char('f') > success('\f')) | - (char('n') > success('\n')) | - (char('r') > success('\r')) | - (char('t') > success('\t')) | - (char('v') > success('\v')) | - (char('\\') > success('\\')) | - (char('"') > success('"')) | - (char("'") > success("'")); + final Parser _escapeCode = (char('a').thenKeep(success('\a'))) + .or(char('b').thenKeep(success('\b'))) + .or(char('f').thenKeep(success('\f'))) + .or(char('n').thenKeep(success('\n'))) + .or(char('r').thenKeep(success('\r'))) + .or(char('t').thenKeep(success('\t'))) + .or(char('v').thenKeep(success('\v'))) + .or(char('\\').thenKeep(success('\\'))) + .or(char('"').thenKeep(success('"'))) + .or(char("'").thenKeep(success("'"))); Parser get _charChar => - (char('\\') > _escapeCode) | pred((c) => c != "'"); + (char('\\').thenKeep(_escapeCode)).or(pred((c) => c != "'")); Parser get charLiteral => lexeme(_charChar.between(char("'"), char("'"))) % 'character literal'; Parser get _stringChar => - (char('\\') > _escapeCode) | pred((c) => c != '"'); + (char('\\').thenKeep(_escapeCode)).or(pred((c) => c != '"')); Parser get stringLiteral => lexeme(_stringChar.many.between(char('"'), char('"'))) .map((cs) => cs.join()) % 'string literal'; - final Parser _hexDigit = oneOf("0123456789abcdefABCDEF"); + final Parser _hexDigit = oneOf('0123456789abcdefABCDEF'); - final Parser _octalDigit = oneOf("01234567"); + final Parser _octalDigit = oneOf('01234567'); - Parser get _maybeSign => (char('-') | char('+')).orElse(''); + Parser get _maybeSign => (char('-').or(char('+'))).orElse(''); - Parser _concat(Parser> parsers) => + Parser _concat(Parser> parsers) => parsers.map((list) => list.join()); - Parser _concatSum(accum) => _concat(accum.list); + Parser _concatSum(accum) => + _concat(accum.list as Parser>); Parser get _decimal => _concat(digit.many1); Parser get _hexaDecimal => - _concatSum(oneOf("xX") + _concat(_hexDigit.many1)); + _concatSum(oneOf('xX').and(_concat(_hexDigit.many1))); Parser get _octal => - _concatSum(oneOf("oO") + _concat(_octalDigit.many1)); + _concatSum(oneOf('oO').and(_concat(_octalDigit.many1))); - Parser get _zeroNumber => - _concat((char('0') + (_hexaDecimal | _octal | _decimal).orElse('')).list); + Parser get _zeroNumber => _concat( + (char('0').and((_hexaDecimal.or(_octal).or(_decimal)).orElse(''))).list); - Parser get _nat => _zeroNumber | _decimal; + Parser get _nat => (_zeroNumber.or(_decimal)); - Parser get _int => _concatSum(lexeme(_maybeSign) + _nat); + Parser get _int => _concatSum(lexeme(_maybeSign).and(_nat)); Parser get _exponent => - _concatSum(oneOf('eE') + _maybeSign + _concat(digit.many1)); + _concatSum(oneOf('eE').and(_maybeSign).and(_concat(digit.many1))); - Parser get _fraction => _concatSum(char('.') + _concat(digit.many1)); + Parser get _fraction => + _concatSum(char('.').and(_concat(digit.many1))); Parser get _fractExponent => - _concatSum(_fraction + _exponent.orElse('')) | _exponent; + (_concatSum(_fraction.and(_exponent.orElse(''))).or(_exponent)); - Parser get _float => _concatSum(decimal + _fractExponent); + Parser get _float => _concatSum(decimal.and(_fractExponent)); - final RegExp _octalPrefix = new RegExp('0[Oo]'); + final RegExp _octalPrefix = RegExp('0[Oo]'); int _parseInt(String str) { if (_octalPrefix.hasMatch(str)) { @@ -876,24 +873,20 @@ class LanguageParsers { Parser get octal => lexeme(_octal).map(_parseInt) % 'octal number'; - /** - * [lexeme] parser for [symb] symbol. - */ + /// [lexeme] parser for [symb] symbol. Parser symbol(String symb) => lexeme(string(symb)); - /** - * Parser combinator which skips whitespaces from the right side. - */ - Parser lexeme(Parser p) => p < whiteSpace; + /// Parser combinator which skips whitespaces from the right side. + Parser lexeme(Parser p) => p.thenDrop(whiteSpace); Parser get _start => string(_commentStart); Parser get _end => string(_commentEnd); - Parser get _multiLineComment => + Parser get _multiLineComment => skipEverythingBetween(_start, _end, nested: _nestedComments); - Parser get _oneLineComment => - string(_commentLine) > (pred((c) => c != '\n').skipMany > success(null)); + Parser get _oneLineComment => string(_commentLine) + .thenKeep(pred((c) => c != '\n').skipMany.thenKeep(success(null))); Parser get whiteSpace => _whiteSpace % 'whitespace/comment'; @@ -901,11 +894,11 @@ class LanguageParsers { if (_commentLine.isEmpty && _commentStart.isEmpty) { return space.skipMany; } else if (_commentLine.isEmpty) { - return (space | _multiLineComment).skipMany; + return (space.or(_multiLineComment)).skipMany; } else if (_commentStart.isEmpty) { - return (space | _oneLineComment).skipMany; + return (space.or(_oneLineComment)).skipMany; } else { - return (space | _oneLineComment | _multiLineComment).skipMany; + return (space.or(_oneLineComment).or(_multiLineComment)).skipMany; } } diff --git a/lib/src/accumulators.dart b/lib/src/accumulators.dart index 1929dcc..de01be2 100644 --- a/lib/src/accumulators.dart +++ b/lib/src/accumulators.dart @@ -1,4 +1,5 @@ part of parsers; +// ignore_for_file: avoid_types_on_closure_parameters class ParserAccumulator2 { final Parser p1; @@ -7,17 +8,17 @@ class ParserAccumulator2 { /// Parser sequencing: creates a parser accumulator ParserAccumulator3 and(Parser p) => - new ParserAccumulator3(p1, p2, p); + ParserAccumulator3(p1, p2, p); /// Alias for [and] ParserAccumulator3 operator +(Parser p) => and(p); /// Action application - Parser map(R f(T1 x1, T2 x2)) => + Parser map(R Function(T1 x1, T2 x2) f) => success((T1 x1) => (T2 x2) => f(x1, x2)).apply(p1).apply(p2); /// Alias for map - Parser operator ^(Object f(T1 x1, T2 x2)) => map(f); + Parser operator ^(Object Function(T1 x1, T2 x2) f) => map(f); /// Creates a [:Parser:] from [this]. Parser get list => @@ -32,20 +33,20 @@ class ParserAccumulator3 { /// Parser sequencing: creates a parser accumulator ParserAccumulator4 and(Parser p) => - new ParserAccumulator4(p1, p2, p3, p); + ParserAccumulator4(p1, p2, p3, p); /// Alias for [and] ParserAccumulator4 operator +(Parser p) => and(p); /// Action application - Parser map(R f(T1 x1, T2 x2, T3 x3)) => + Parser map(R Function(T1 x1, T2 x2, T3 x3) f) => success((T1 x1) => (T2 x2) => (T3 x3) => f(x1, x2, x3)) .apply(p1) .apply(p2) .apply(p3); /// Alias for map - Parser operator ^(Object f(T1 x1, T2 x2, T3 x3)) => map(f); + Parser operator ^(Object Function(T1 x1, T2 x2, T3 x3) f) => map(f); /// Creates a [:Parser:] from [this]. Parser get list => @@ -64,13 +65,13 @@ class ParserAccumulator4 { /// Parser sequencing: creates a parser accumulator ParserAccumulator5 and(Parser p) => - new ParserAccumulator5(p1, p2, p3, p4, p); + ParserAccumulator5(p1, p2, p3, p4, p); /// Alias for [and] ParserAccumulator5 operator +(Parser p) => and(p); /// Action application - Parser map(R f(T1 x1, T2 x2, T3 x3, T4 x4)) => + Parser map(R Function(T1 x1, T2 x2, T3 x3, T4 x4) f) => success((T1 x1) => (T2 x2) => (T3 x3) => (T4 x4) => f(x1, x2, x3, x4)) .apply(p1) .apply(p2) @@ -78,7 +79,7 @@ class ParserAccumulator4 { .apply(p4); /// Alias for map - Parser operator ^(Object f(T1 x1, T2 x2, T3 x3, T4 x4)) => map(f); + Parser operator ^(Object Function(T1 x1, T2 x2, T3 x3, T4 x4) f) => map(f); /// Creates a [:Parser:] from [this]. Parser get list => @@ -99,13 +100,13 @@ class ParserAccumulator5 { /// Parser sequencing: creates a parser accumulator ParserAccumulator6 and(Parser p) => - new ParserAccumulator6(p1, p2, p3, p4, p5, p); + ParserAccumulator6(p1, p2, p3, p4, p5, p); /// Alias for [and] ParserAccumulator6 operator +(Parser p) => and(p); /// Action application - Parser map(R f(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5)) => success((T1 x1) => + Parser map(R Function(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5) f) => success((T1 x1) => (T2 x2) => (T3 x3) => (T4 x4) => (T5 x5) => f(x1, x2, x3, x4, x5)) .apply(p1) .apply(p2) @@ -114,7 +115,7 @@ class ParserAccumulator5 { .apply(p5); /// Alias for map - Parser operator ^(Object f(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5)) => map(f); + Parser operator ^(Object Function(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5) f) => map(f); /// Creates a [:Parser:] from [this]. Parser get list => success((T1 x1) => @@ -137,13 +138,13 @@ class ParserAccumulator6 { /// Parser sequencing: creates a parser accumulator ParserAccumulator7 and(Parser p) => - new ParserAccumulator7(p1, p2, p3, p4, p5, p6, p); + ParserAccumulator7(p1, p2, p3, p4, p5, p6, p); /// Alias for [and] ParserAccumulator7 operator +(Parser p) => and(p); /// Action application - Parser map(R f(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6)) => + Parser map(R Function(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6) f) => success((T1 x1) => (T2 x2) => (T3 x3) => (T4 x4) => (T5 x5) => (T6 x6) => f(x1, x2, x3, x4, x5, x6)) .apply(p1) @@ -154,7 +155,7 @@ class ParserAccumulator6 { .apply(p6); /// Alias for map - Parser operator ^(Object f(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6)) => + Parser operator ^(Object Function(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6) f) => map(f); /// Creates a [:Parser:] from [this]. @@ -181,13 +182,13 @@ class ParserAccumulator7 { /// Parser sequencing: creates a parser accumulator ParserAccumulator8 and(Parser p) => - new ParserAccumulator8(p1, p2, p3, p4, p5, p6, p7, p); + ParserAccumulator8(p1, p2, p3, p4, p5, p6, p7, p); /// Alias for [and] ParserAccumulator8 operator +(Parser p) => and(p); /// Action application - Parser map(R f(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7)) => + Parser map(R Function(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7) f) => success((T1 x1) => (T2 x2) => (T3 x3) => (T4 x4) => (T5 x5) => (T6 x6) => (T7 x7) => f(x1, x2, x3, x4, x5, x6, x7)) .apply(p1) @@ -200,7 +201,7 @@ class ParserAccumulator7 { /// Alias for map Parser operator ^( - Object f(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7)) => + Object Function(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7) f) => map(f); /// Creates a [:Parser:] from [this]. @@ -230,14 +231,14 @@ class ParserAccumulator8 { /// Parser sequencing: creates a parser accumulator ParserAccumulator9 and( Parser p) => - new ParserAccumulator9(p1, p2, p3, p4, p5, p6, p7, p8, p); + ParserAccumulator9(p1, p2, p3, p4, p5, p6, p7, p8, p); /// Alias for [and] ParserAccumulator9 operator +(Parser p) => and(p); /// Action application Parser map( - R f(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7, T8 x8)) => + R Function(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7, T8 x8) f) => success((T1 x1) => (T2 x2) => (T3 x3) => (T4 x4) => (T5 x5) => (T6 x6) => (T7 x7) => (T8 x8) => f(x1, x2, x3, x4, x5, x6, x7, x8)) .apply(p1) @@ -251,7 +252,7 @@ class ParserAccumulator8 { /// Alias for map Parser operator ^( - Object f(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7, T8 x8)) => + Object Function(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7, T8 x8) f) => map(f); /// Creates a [:Parser:] from [this]. @@ -284,14 +285,14 @@ class ParserAccumulator9 { /// Parser sequencing: creates a parser accumulator ParserAccumulator10 and( Parser p) => - new ParserAccumulator10(p1, p2, p3, p4, p5, p6, p7, p8, p9, p); + ParserAccumulator10(p1, p2, p3, p4, p5, p6, p7, p8, p9, p); /// Alias for [and] ParserAccumulator10 operator +(Parser p) => and(p); /// Action application Parser map( - R f(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7, T8 x8, T9 x9)) => + R Function(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7, T8 x8, T9 x9) f) => success((T1 x1) => (T2 x2) => (T3 x3) => (T4 x4) => (T5 x5) => (T6 x6) => (T7 x7) => (T8 x8) => (T9 x9) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9)) @@ -307,8 +308,8 @@ class ParserAccumulator9 { /// Alias for map Parser operator ^( - Object f( - T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7, T8 x8, T9 x9)) => + Object Function( + T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7, T8 x8, T9 x9) f) => map(f); /// Creates a [:Parser:] from [this]. @@ -342,8 +343,8 @@ class ParserAccumulator10 { /// Action application Parser map( - R f(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7, T8 x8, T9 x9, - T10 x10)) => + R Function(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7, T8 x8, T9 x9, + T10 x10) f) => success((T1 x1) => (T2 x2) => (T3 x3) => (T4 x4) => (T5 x5) => (T6 x6) => (T7 x7) => (T8 x8) => (T9 x9) => (T10 x10) => f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)) @@ -360,8 +361,8 @@ class ParserAccumulator10 { /// Alias for map Parser operator ^( - Object f(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7, T8 x8, - T9 x9, T10 x10)) => + Object Function(T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7, T8 x8, + T9 x9, T10 x10) f) => map(f); /// Creates a [:Parser:] from [this]. diff --git a/pubspec.yaml b/pubspec.yaml index 16e7bdc..2735a1b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,14 +1,16 @@ name: parsers -version: 1.0.0 -author: Paul Brauner +version: 2.0.0 +authors: + - Paul Brauner + - Dinesh Ahuja description: > Parser combinators for Dart. Parser combinators let you write complex parsers by combining smaller, less complex parsers. homepage: https://github.com/polux/parsers documentation: http://goo.gl/ZNqdEH environment: - sdk: '>=0.8.10+6 <2.0.0' + sdk: '>=2.0.0 <3.0.0' dependencies: - persistent: '^1.0.0' + # persistent: any dev_dependencies: - unittest: '^0.11.0' + test: ^1.0.0 diff --git a/test/parsers_test.dart b/test/parsers_test.dart index ed02a31..ba19036 100644 --- a/test/parsers_test.dart +++ b/test/parsers_test.dart @@ -5,12 +5,13 @@ // Paul Brauner (polux@google.com) // Adam Singer (financecoding@gmail.com) // Maxim Dikun (me@dikmax.name) +// Dinesh Ahuja (dev@kabiir.me) library parsers_test; import 'package:parsers/parsers.dart'; -import 'package:persistent/persistent.dart'; -import 'package:unittest/unittest.dart'; + +import 'package:test/test.dart'; part 'src/parsers_model.dart'; @@ -22,13 +23,13 @@ class FailureMatcher extends Matcher { FailureMatcher(this.rest); bool matches(parseResult, Map matchState) { - return parseResult is ParseResult - && !parseResult.isSuccess - && _rest(parseResult) == rest; + return parseResult is ParseResult && + !parseResult.isSuccess && + _rest(parseResult) == rest; } Description describe(Description description) => - description.add('a parse failure with rest "$rest"'); + description.add('a parse failure with rest "$rest"'); } class SuccessMatcher extends Matcher { @@ -38,19 +39,19 @@ class SuccessMatcher extends Matcher { SuccessMatcher(this.res, this.rest); bool matches(parseResult, Map matchState) { - return parseResult is ParseResult - && parseResult.isSuccess - && equals(parseResult.value).matches(res, null) - && parseResult.text.substring(parseResult.position.offset) == rest; + return parseResult is ParseResult && + parseResult.isSuccess && + equals(parseResult.value).matches(res, null) && + parseResult.text.substring(parseResult.position.offset) == rest; } Description describe(Description description) => - description.add('a parse success with value $res and rest "$rest"'); + description.add('a parse success with value $res and rest "$rest"'); } -isFailure(rest) => new FailureMatcher(rest); +isFailure(rest) => FailureMatcher(rest as String); -isSuccess(res, rest) => new SuccessMatcher(res, rest); +isSuccess(res, rest) => SuccessMatcher(res, rest as String); checkFloat(res, f, rest) { expect(res.isSuccess, isTrue); @@ -60,68 +61,74 @@ checkFloat(res, f, rest) { checkList(res, list, rest) { expect(res.isSuccess, isTrue); - expect(res.value, orderedEquals(list)); + expect(res.value, orderedEquals(list as Iterable)); expect(res.value.rest, equals(rest)); } main() { - test('position 1', () => - expect(new Position(1, 1, 1, tabStop: 4).addChar("\t").character, equals(5))); - test('position 2', () => - expect(new Position(1, 1, 2, tabStop: 4).addChar("\t").character, equals(5))); - test('position 3', () => - expect(new Position(1, 1, 4, tabStop: 4).addChar("\t").character, equals(5))); - test('position 4', () => - expect(new Position(1, 1, 5, tabStop: 4).addChar("\t").character, equals(9))); - test('position 5', () => - expect(new Position(1, 1, 3).addChar("\t").character, equals(4))); + test( + 'position 1', + () => expect( + Position(1, 1, 1, tabStop: 4).addChar('\t').character, equals(5))); + test( + 'position 2', + () => expect( + Position(1, 1, 2, tabStop: 4).addChar('\t').character, equals(5))); + test( + 'position 3', + () => expect( + Position(1, 1, 4, tabStop: 4).addChar('\t').character, equals(5))); + test( + 'position 4', + () => expect( + Position(1, 1, 5, tabStop: 4).addChar('\t').character, equals(9))); + test('position 5', + () => expect(Position(1, 1, 3).addChar('\t').character, equals(4))); - test('char 1', () => - expect(char('a').run('abc'), isSuccess('a', 'bc'))); + test('char 1', () => expect(char('a').run('abc'), isSuccess('a', 'bc'))); - test('char 2', () => - expect(char('a').run('a'), isSuccess('a', ''))); + test('char 2', () => expect(char('a').run('a'), isSuccess('a', ''))); - test('char 3', () => - expect(char('a').run('bac'), isFailure('bac'))); + test('char 3', () => expect(char('a').run('bac'), isFailure('bac'))); - test('char 4', () => - expect(char('a').run('b'), isFailure('b'))); + test('char 4', () => expect(char('a').run('b'), isFailure('b'))); - test('char 5', () => - expect(char('a').run(''), isFailure(''))); + test('char 5', () => expect(char('a').run(''), isFailure(''))); - test('string 1', () => - expect(string('').run('abc'), isSuccess('', 'abc'))); + test('string 1', () => expect(string('').run('abc'), isSuccess('', 'abc'))); - test('string 2', () => - expect(string('foo').run('fooabc'), isSuccess('foo', 'abc'))); + test('string 2', + () => expect(string('foo').run('fooabc'), isSuccess('foo', 'abc'))); - test('string 3', () => - expect(string('foo').run('barabc'), isFailure('barabc'))); + test('string 3', + () => expect(string('foo').run('barabc'), isFailure('barabc'))); - test('string 4', () => - expect(string('foo').run('fo'), isFailure('fo'))); + test('string 4', () => expect(string('foo').run('fo'), isFailure('fo'))); - test('string 5', () => - expect(string('foo').run('foo'), isSuccess('foo', ''))); + test( + 'string 5', () => expect(string('foo').run('foo'), isSuccess('foo', ''))); - test('> 1', () => - expect((char('a') > char('b')).run('abc'), isSuccess('b','c'))); + test( + '> 1', + () => expect( + (char('a').thenKeep(char('b'))).run('abc'), isSuccess('b', 'c'))); - test('> 2', () => - expect((char('a') > char('b')).run('bbc'), isFailure('bbc'))); + test( + '> 2', + () => + expect((char('a').thenKeep(char('b'))).run('bbc'), isFailure('bbc'))); - test('> 3', () => - expect((char('a') > char('b')).run('aac'), isFailure('ac'))); + test( + '> 3', + () => + expect((char('a').thenKeep(char('b'))).run('aac'), isFailure('ac'))); - final let = string("let").notFollowedBy(alphanum); + final let = string('let').notFollowedBy(alphanum); - test('notFollowedBy 1', () => - expect(let.run('let aa'), isSuccess('let', ' aa'))); + test('notFollowedBy 1', + () => expect(let.run('let aa'), isSuccess('let', ' aa'))); - test('notFollowedBy 2', () => - expect(let.run('letaa'), isFailure('aa'))); + test('notFollowedBy 2', () => expect(let.run('letaa'), isFailure('aa'))); many1Prop(f) => expect(f(char('a')).run(''), isSuccess([], '')); @@ -153,782 +160,807 @@ main() { test('skipMany 3 model', () => skipMany3Prop(skipManyModel)); test('skipMany 3 impl', () => skipMany3Prop(skipManyModel)); - test('maybe 1', () => - expect(char('a').maybe.run('a'), isSuccess(new Option.some('a'),''))); + test('maybe 1', + () => expect(char('a').maybe.run('a'), isSuccess(Option.some('a'), ''))); - test('maybe 2', () => - expect(char('a').maybe.run('b'), isSuccess(new Option.none(),'b'))); + test('maybe 2', + () => expect(char('a').maybe.run('b'), isSuccess(Option.none(), 'b'))); - test('sepBy 1', () => - expect(char('a').sepBy(char(';')).run('a;a;a'), - isSuccess(['a', 'a', 'a'], ''))); + test( + 'sepBy 1', + () => expect(char('a').sepBy(char(';')).run('a;a;a'), + isSuccess(['a', 'a', 'a'], ''))); - test('sepBy 2', () => - expect(char('a').sepBy(char(';')).run('a;a;a;'), - isSuccess(['a', 'a', 'a'], ';'))); + test( + 'sepBy 2', + () => expect(char('a').sepBy(char(';')).run('a;a;a;'), + isSuccess(['a', 'a', 'a'], ';'))); - test('sepBy 3', () => - expect(char('a').sepBy(char(';')).run(''), - isSuccess([], ''))); + test('sepBy 3', + () => expect(char('a').sepBy(char(';')).run(''), isSuccess([], ''))); - test('sepBy 4', () => - expect(char('a').sepBy(char(';')).run(';'), - isSuccess([], ';'))); + test('sepBy 4', + () => expect(char('a').sepBy(char(';')).run(';'), isSuccess([], ';'))); - test('sepBy 5', () => - expect(char('a').sepBy(char(';')).run(''), - isSuccess([], ''))); + test('sepBy 5', + () => expect(char('a').sepBy(char(';')).run(''), isSuccess([], ''))); - test('sepBy1 1', () => - expect(char('a').sepBy1(char(';')).run('a;a'), - isSuccess(['a','a'], ''))); + test( + 'sepBy1 1', + () => expect( + char('a').sepBy1(char(';')).run('a;a'), isSuccess(['a', 'a'], ''))); - test('sepBy1 2', () => - expect(char('a').sepBy1(char(';')).run('a;a;'), - isSuccess(['a','a'], ';'))); + test( + 'sepBy1 2', + () => expect( + char('a').sepBy1(char(';')).run('a;a;'), isSuccess(['a', 'a'], ';'))); - test('sepBy1 3', () => - expect(char('a').sepBy1(char(';')).run(''), - isFailure(''))); + test('sepBy1 3', + () => expect(char('a').sepBy1(char(';')).run(''), isFailure(''))); - test('sepBy1 4', () => - expect(char('a').sepEndBy1(char(';')).run(';'), - isFailure(';'))); + test('sepBy1 4', + () => expect(char('a').sepEndBy1(char(';')).run(';'), isFailure(';'))); - test('sepEndBy 1', () => - expect(char('a').sepEndBy(char(';')).run('a;a;a'), - isSuccess(['a', 'a', 'a'], ''))); + test( + 'sepEndBy 1', + () => expect(char('a').sepEndBy(char(';')).run('a;a;a'), + isSuccess(['a', 'a', 'a'], ''))); - test('sepEndBy 2', () => - expect(char('a').sepEndBy(char(';')).run('a;a;a;'), - isSuccess(['a', 'a', 'a'], ''))); + test( + 'sepEndBy 2', + () => expect(char('a').sepEndBy(char(';')).run('a;a;a;'), + isSuccess(['a', 'a', 'a'], ''))); - test('sepEndBy 3', () => - expect(char('a').sepEndBy(char(';')).run(''), - isSuccess([], ''))); + test('sepEndBy 3', + () => expect(char('a').sepEndBy(char(';')).run(''), isSuccess([], ''))); - test('sepEndBy 4', () => - expect(char('a').sepEndBy(char(';')).run(';'), - isSuccess([], ';'))); + test('sepEndBy 4', + () => expect(char('a').sepEndBy(char(';')).run(';'), isSuccess([], ';'))); - test('sepEndBy1 1', () => - expect(char('a').sepEndBy1(char(';')).run('a;a'), - isSuccess(['a','a'], ''))); + test( + 'sepEndBy1 1', + () => expect(char('a').sepEndBy1(char(';')).run('a;a'), + isSuccess(['a', 'a'], ''))); - test('sepEndBy1 2', () => - expect(char('a').sepEndBy1(char(';')).run('a;a;'), - isSuccess(['a','a'], ''))); + test( + 'sepEndBy1 2', + () => expect(char('a').sepEndBy1(char(';')).run('a;a;'), + isSuccess(['a', 'a'], ''))); - test('sepEndBy1 3', () => - expect(char('a').sepEndBy1(char(';')).run(''), isFailure(''))); + test('sepEndBy1 3', + () => expect(char('a').sepEndBy1(char(';')).run(''), isFailure(''))); - test('sepEndBy1 4', () => - expect(char('a').sepEndBy1(char(';')).run(';'), isFailure(';'))); + test('sepEndBy1 4', + () => expect(char('a').sepEndBy1(char(';')).run(';'), isFailure(';'))); - test('letter', () => - expect(letter.run('a'), isSuccess('a', ''))); + test('letter', () => expect(letter.run('a'), isSuccess('a', ''))); - manyUntilProp0(f) => - expect((string('/*') > f(anyChar, string('*/'))).run('/* abcdef */'), - isSuccess(' abcdef '.split(''), '')); + manyUntilProp0(Parser Function(Parser, Parser) f) => expect( + (string('/*').thenKeep((f(anyChar, string('*/'))))).run('/* abcdef */'), + isSuccess(' abcdef '.split(''), '')); test('manyUntil 0 model', () => manyUntilProp0(manyUntilModel)); test('manyUntil 0 impl', () => manyUntilProp0(manyUntilImpl)); - manyUntilProp1(f) => - expect(f(anyChar, string('*/')).run(' a b c d */ e'), - isSuccess(' a b c d '.split(''), ' e')); + manyUntilProp1(f) => expect(f(anyChar, string('*/')).run(' a b c d */ e'), + isSuccess(' a b c d '.split(''), ' e')); test('manyUntil 1 model', () => manyUntilProp1(manyUntilModel)); test('manyUntil 1 impl', () => manyUntilProp1(manyUntilImpl)); manyUntilProp2(f) => - expect(f(anyChar, string('*/')).run(' a b c d e'), - isFailure('')); + expect(f(anyChar, string('*/')).run(' a b c d e'), isFailure('')); test('manyUntil 2 model', () => manyUntilProp2(manyUntilModel)); test('manyUntil 2 impl', () => manyUntilProp2(manyUntilImpl)); - manyUntilProp3(f) => - expect(f(anyChar, string('*/').lookAhead).run(' a b c d */ e'), - isSuccess(' a b c d '.split(''), '*/ e')); + manyUntilProp3(f) => expect( + f(anyChar, string('*/').lookAhead).run(' a b c d */ e'), + isSuccess(' a b c d '.split(''), '*/ e')); test('manyUntil 3 model', () => manyUntilProp3(manyUntilModel)); test('manyUntil 3 impl', () => manyUntilProp3(manyUntilImpl)); - skipManyUntilProp1(f) => - expect(f(anyChar, string('*/')).run(' a b c d */ e'), - isSuccess(null, ' e')); + skipManyUntilProp1(f) => expect( + f(anyChar, string('*/')).run(' a b c d */ e'), isSuccess(null, ' e')); test('skipManyUntil 1 model', () => skipManyUntilProp1(skipManyUntilModel)); test('skipManyUntil 1 impl', () => skipManyUntilProp1(skipManyUntilImpl)); skipManyUntilProp2(f) => - expect(f(anyChar, string('*/')).run(' a b c d e'), - isFailure('')); + expect(f(anyChar, string('*/')).run(' a b c d e'), isFailure('')); test('skipManyUntil 2 model', () => skipManyUntilProp2(skipManyUntilModel)); test('skipManyUntil 2 impl', () => skipManyUntilProp2(skipManyUntilImpl)); - skipManyUntilProp3(f) => - expect(f(anyChar, string('*/').lookAhead).run(' a b c d */ e'), - isSuccess(null, '*/ e')); + skipManyUntilProp3(f) => expect( + f(anyChar, string('*/').lookAhead).run(' a b c d */ e'), + isSuccess(null, '*/ e')); test('skipManyUntil 3 model', () => skipManyUntilProp3(skipManyUntilModel)); test('skipManyUntil 3 impl', () => skipManyUntilProp3(skipManyUntilImpl)); - final lang = new LanguageParsers( - nestedComments: true, - reservedNames: ['for', 'in']); - - test('semi 1', () => - expect(lang.semi.run(';rest'), isSuccess(';', 'rest'))); - - test('semi 2', () => - expect(lang.semi.run('a'), isFailure('a'))); - - test('comma 1', () => - expect(lang.comma.run(',rest'), isSuccess(',', 'rest'))); - - test('comma 2', () => - expect(lang.comma.run('a'), isFailure('a'))); - - test('colon 1', () => - expect(lang.colon.run(':rest'), isSuccess(':', 'rest'))); - - test('colon 2', () => - expect(lang.colon.run('a'), isFailure('a'))); - - test('dot 1', () => - expect(lang.dot.run('.rest'), isSuccess('.', 'rest'))); - - test('dot 2', () => - expect(lang.dot.run('a'), isFailure('a'))); - - test('identifier 1', () => - expect(lang.identifier.run('BarFoo toto'), - isSuccess('BarFoo', 'toto'))); - - test('identifier 2', () => - expect(lang.identifier.run('B6ar_Foo toto'), - isSuccess('B6ar_Foo', 'toto'))); - - test('identifier 3', () => - expect(lang.identifier.run('B6ar_Foo toto'), - isSuccess('B6ar_Foo', 'toto'))); + final lang = + LanguageParsers(nestedComments: true, reservedNames: ['for', 'in']); + + test('semi 1', () => expect(lang.semi.run(';rest'), isSuccess(';', 'rest'))); + + test('semi 2', () => expect(lang.semi.run('a'), isFailure('a'))); + + test( + 'comma 1', () => expect(lang.comma.run(',rest'), isSuccess(',', 'rest'))); + + test('comma 2', () => expect(lang.comma.run('a'), isFailure('a'))); + + test( + 'colon 1', () => expect(lang.colon.run(':rest'), isSuccess(':', 'rest'))); + + test('colon 2', () => expect(lang.colon.run('a'), isFailure('a'))); + + test('dot 1', () => expect(lang.dot.run('.rest'), isSuccess('.', 'rest'))); + + test('dot 2', () => expect(lang.dot.run('a'), isFailure('a'))); + + test( + 'identifier 1', + () => expect( + lang.identifier.run('BarFoo toto'), isSuccess('BarFoo', 'toto'))); + + test( + 'identifier 2', + () => expect( + lang.identifier.run('B6ar_Foo toto'), isSuccess('B6ar_Foo', 'toto'))); + + test( + 'identifier 3', + () => expect( + lang.identifier.run('B6ar_Foo toto'), isSuccess('B6ar_Foo', 'toto'))); + + test( + 'identifier 4', + () => expect( + lang.identifier.run('7B6ar_Foo toto'), isFailure('7B6ar_Foo toto'))); + + test( + 'identifier 5', + () => expect(lang.identifier.run('_7B6ar_Foo toto'), + isSuccess('_7B6ar_Foo', 'toto'))); + + test( + 'identifier 6', + () => expect(lang.identifier.sepBy(lang.comma).run('abc, def, hij'), + isSuccess(['abc', 'def', 'hij'], ''))); + + test( + 'multi-line comment 1.1', + () => expect( + (lang.identifier.thenKeep(lang.identifier)).run('a /* abc */ b'), + isSuccess('b', ''))); - test('identifier 4', () => - expect(lang.identifier.run('7B6ar_Foo toto'), - isFailure('7B6ar_Foo toto'))); + test( + 'multi-line comment 1.2', + () => expect( + (lang.identifier.thenKeep(lang.identifier)) + .run('a /* x /* abc */ y */ b'), + isSuccess('b', ''))); - test('identifier 5', () => - expect(lang.identifier.run('_7B6ar_Foo toto'), - isSuccess('_7B6ar_Foo', 'toto'))); + test( + 'multi-line comment 1.3', + () => expect( + (lang.identifier.thenKeep(lang.identifier)) + .run('a /* x /* abc */ y b'), + isFailure('/* x /* abc */ y b'))); - test('identifier 6', () => - expect(lang.identifier.sepBy(lang.comma).run('abc, def, hij'), - isSuccess(['abc', 'def', 'hij'], ''))); + test( + 'multi-line comment 1.4', + () => expect( + (lang.identifier.thenKeep(lang.identifier)).run('a /*/**/*/ y b'), + isSuccess('y', 'b'))); - test('multi-line comment 1.1', () => - expect((lang.identifier > lang.identifier).run('a /* abc */ b'), - isSuccess('b', ''))); + test( + 'multi-line comment 1.5', + () => expect( + (lang.identifier.thenKeep(lang.identifier)).run('a /*/**/ y b'), + isFailure('/*/**/ y b'))); - test('multi-line comment 1.2', () => - expect((lang.identifier > lang.identifier).run('a /* x /* abc */ y */ b'), - isSuccess('b', ''))); + test( + 'single-line comment 1.1', + () => expect( + (lang.identifier.thenKeep(lang.identifier)).run('a // foo \n b'), + isSuccess('b', ''))); - test('multi-line comment 1.3', () => - expect((lang.identifier > lang.identifier).run('a /* x /* abc */ y b'), - isFailure('/* x /* abc */ y b'))); + final noNest = LanguageParsers(nestedComments: false); - test('multi-line comment 1.4', () => - expect((lang.identifier > lang.identifier).run('a /*/**/*/ y b'), - isSuccess('y', 'b'))); + test( + 'multi-line comment 2.1', + () => expect( + (noNest.identifier.thenKeep(lang.identifier)).run('a /* abc */ b'), + isSuccess('b', ''))); - test('multi-line comment 1.5', () => - expect((lang.identifier > lang.identifier).run('a /*/**/ y b'), - isFailure('/*/**/ y b'))); + test( + 'multi-line comment 2.2', + () => expect( + (noNest.identifier.thenKeep(lang.identifier)) + .run('a /* x /* abc */ y */ b'), + isSuccess('y', '*/ b'))); - test('single-line comment 1.1', () => - expect((lang.identifier > lang.identifier).run('a // foo \n b'), - isSuccess('b', ''))); + test( + 'multi-line comment 2.3', + () => expect( + (noNest.identifier.thenKeep(lang.identifier)) + .run('a /* x /* abc */ y b'), + isSuccess('y', 'b'))); - final noNest = new LanguageParsers(nestedComments: false); + test( + 'multi-line comment 2.4', + () => expect( + (noNest.identifier.thenKeep(lang.identifier)).run('a /*/**/*/ y b'), + isFailure('*/ y b'))); - test('multi-line comment 2.1', () => - expect((noNest.identifier > lang.identifier).run('a /* abc */ b'), - isSuccess('b', ''))); + test( + 'multi-line comment 2.5', + () => expect( + (noNest.identifier.thenKeep(lang.identifier)).run('a /*/**/ y b'), + isSuccess('y', 'b'))); - test('multi-line comment 2.2', () => - expect((noNest.identifier > lang.identifier).run( - 'a /* x /* abc */ y */ b'), - isSuccess('y', '*/ b'))); + test( + 'single-line comment 2.1', + () => expect( + (lang.identifier.thenKeep(lang.identifier)).run('a // foo \n b'), + isSuccess('b', ''))); - test('multi-line comment 2.3', () => - expect((noNest.identifier > lang.identifier).run('a /* x /* abc */ y b'), - isSuccess('y', 'b'))); + test('reserved 1', + () => expect(lang.reserved['for'].run('for a'), isSuccess('for', 'a'))); - test('multi-line comment 2.4', () => - expect((noNest.identifier > lang.identifier).run('a /*/**/*/ y b'), - isFailure('*/ y b'))); + test('reserved 2', + () => expect(lang.reserved['for'].run('fora'), isFailure('a'))); - test('multi-line comment 2.5', () => - expect((noNest.identifier > lang.identifier).run('a /*/**/ y b'), - isSuccess('y', 'b'))); + test('reserved 3', + () => expect(() => lang.reserved['foo'].run('fora'), throwsA(anything))); - test('single-line comment 2.1', () => - expect((lang.identifier > lang.identifier).run('a // foo \n b'), - isSuccess('b', ''))); + test( + 'char 1', () => expect(lang.charLiteral.run(r"'a'"), isSuccess('a', ''))); - test('reserved 1', () => - expect(lang.reserved['for'].run('for a'), isSuccess('for', 'a'))); + test('char 2', () => expect(lang.charLiteral.run(r"'aa'"), isFailure("a'"))); - test('reserved 2', () => - expect(lang.reserved['for'].run('fora'), isFailure('a'))); + test('char 3', () => expect(lang.charLiteral.run(r"''"), isFailure("'"))); - test('reserved 3', () => - expect(() => lang.reserved['foo'].run('fora'), throws)); + test('char 4', + () => expect(lang.charLiteral.run(r"'\t'"), isSuccess('\t', ''))); - test('char 1', () => - expect(lang.charLiteral.run(r"'a'"), isSuccess('a', ''))); + test('char 5', + () => expect(lang.charLiteral.run(r"'\\'"), isSuccess(r'\', ''))); - test('char 2', () => - expect(lang.charLiteral.run(r"'aa'"), isFailure("a'"))); + test('string 1', + () => expect(lang.stringLiteral.run(r'"aaa"'), isSuccess('aaa', ''))); - test('char 3', () => - expect(lang.charLiteral.run(r"''"), isFailure("'"))); + test('string 2', + () => expect(lang.stringLiteral.run(r'"a\ta"'), isSuccess('a\ta', ''))); - test('char 4', () => - expect(lang.charLiteral.run(r"'\t'"), isSuccess('\t', ''))); + test('string 3', + () => expect(lang.stringLiteral.run(r'"a\\a"'), isSuccess(r'a\a', ''))); - test('char 5', () => - expect(lang.charLiteral.run(r"'\\'"), isSuccess(r'\', ''))); + test('string 4', + () => expect(lang.stringLiteral.run(r'"a"a"'), isSuccess(r'a', 'a"'))); - test('string 1', () => - expect(lang.stringLiteral.run(r'"aaa"'), isSuccess('aaa', ''))); + test('natural 1', () => expect(lang.natural.run('42'), isSuccess(42, ''))); - test('string 2', () => - expect(lang.stringLiteral.run(r'"a\ta"'), isSuccess('a\ta', ''))); + test('natural 2', () => expect(lang.natural.run('0O42'), isSuccess(34, ''))); - test('string 3', () => - expect(lang.stringLiteral.run(r'"a\\a"'), isSuccess(r'a\a', ''))); + test('natural 3', () => expect(lang.natural.run('0o42'), isSuccess(34, ''))); - test('string 4', () => - expect(lang.stringLiteral.run(r'"a"a"'), isSuccess(r'a', 'a"'))); + test('natural 4', () => expect(lang.natural.run('0X42'), isSuccess(66, ''))); - test('natural 1', () => - expect(lang.natural.run('42'), isSuccess(42, ''))); + test('natural 5', () => expect(lang.natural.run('0xFf'), isSuccess(255, ''))); - test('natural 2', () => - expect(lang.natural.run('0O42'), isSuccess(34, ''))); + test( + 'natural 6', () => expect(lang.natural.run('-0x42'), isFailure('-0x42'))); - test('natural 3', () => - expect(lang.natural.run('0o42'), isSuccess(34, ''))); + test('decimal 1', () => expect(lang.decimal.run('42'), isSuccess(42, ''))); - test('natural 4', () => - expect(lang.natural.run('0X42'), isSuccess(66, ''))); + test( + 'decimal 2', () => expect(lang.decimal.run('-0x42'), isFailure('-0x42'))); - test('natural 5', () => - expect(lang.natural.run('0xFf'), isSuccess(255, ''))); + test('int 1', () => expect(lang.intLiteral.run('-0x42'), isSuccess(-66, ''))); - test('natural 6', () => - expect(lang.natural.run('-0x42'), isFailure('-0x42'))); + test('int 2', + () => expect(lang.intLiteral.run('- 0x42'), isSuccess(-66, ''))); - test('decimal 1', () => - expect(lang.decimal.run('42'), isSuccess(42, ''))); + test('int 3', () => expect(lang.intLiteral.run('1'), isSuccess(1, ''))); - test('decimal 2', () => - expect(lang.decimal.run('-0x42'), isFailure('-0x42'))); + test('int 4', () => expect(lang.intLiteral.run(' 1'), isSuccess(1, ''))); - test('int 1', () => - expect(lang.intLiteral.run('-0x42'), isSuccess(-66, ''))); + test('int 5', + () => expect(lang.intLiteral.run('6492 '), isSuccess(6492, ''))); - test('int 2', () => - expect(lang.intLiteral.run('- 0x42'), isSuccess(-66, ''))); + test('float 1', + () => expect(lang.floatLiteral.run('3.14'), isSuccess(3.14, ''))); - test('int 3', () => - expect(lang.intLiteral.run('1'), isSuccess(1, ''))); + test('float 2', + () => expect(lang.floatLiteral.run('3.14e5'), isSuccess(314000.0, ''))); - test('int 4', () => - expect(lang.intLiteral.run(' 1'), isSuccess(1, ''))); + test('float 3', + () => expect(lang.floatLiteral.run('3.14e-5'), isSuccess(3.14e-5, ''))); - test('int 5', () => - expect(lang.intLiteral.run('6492 '), isSuccess(6492, ''))); + test( + 'parens 1', + () => expect(lang.parens(string('abc')).run('(abc)rest'), + isSuccess('abc', 'rest'))); - test('float 1', () => - expect(lang.floatLiteral.run('3.14'), isSuccess(3.14, ''))); + test('parens 2', + () => expect(lang.parens(string('abc')).run('abc)'), isFailure('abc)'))); - test('float 2', () => - expect(lang.floatLiteral.run('3.14e5'), isSuccess(314000.0, ''))); + test('parens 3', + () => expect(lang.parens(string('abc')).run('(abc'), isFailure(''))); - test('float 3', () => - expect(lang.floatLiteral.run('3.14e-5'), isSuccess(3.14e-5, ''))); + test( + 'braces 1', + () => expect(lang.braces(string('abc')).run('{abc}rest'), + isSuccess('abc', 'rest'))); - test('parens 1', () => - expect(lang.parens(string('abc')).run('(abc)rest'), - isSuccess('abc', 'rest'))); + test('braces 2', + () => expect(lang.braces(string('abc')).run('abc}'), isFailure('abc}'))); - test('parens 2', () => - expect(lang.parens(string('abc')).run('abc)'), isFailure('abc)'))); + test('braces 3', + () => expect(lang.braces(string('abc')).run('{abc'), isFailure(''))); - test('parens 3', () => - expect(lang.parens(string('abc')).run('(abc'), isFailure(''))); + test( + 'angles 1', + () => expect(lang.angles(string('abc')).run('rest'), + isSuccess('abc', 'rest'))); - test('braces 1', () => - expect(lang.braces(string('abc')).run('{abc}rest'), - isSuccess('abc', 'rest'))); + test('angles 2', + () => expect(lang.angles(string('abc')).run('abc>'), isFailure('abc>'))); - test('braces 2', () => - expect(lang.braces(string('abc')).run('abc}'), isFailure('abc}'))); + test('angles 3', + () => expect(lang.angles(string('abc')).run(' - expect(lang.braces(string('abc')).run('{abc'), isFailure(''))); + test( + 'brackets 1', + () => expect(lang.brackets(string('abc')).run('[abc]rest'), + isSuccess('abc', 'rest'))); - test('angles 1', () => - expect(lang.angles(string('abc')).run('rest'), - isSuccess('abc', 'rest'))); + test( + 'brackets 2', + () => + expect(lang.brackets(string('abc')).run('abc]'), isFailure('abc]'))); - test('angles 2', () => - expect(lang.angles(string('abc')).run('abc>'), isFailure('abc>'))); + test('brackets 3', + () => expect(lang.brackets(string('abc')).run('[abc'), isFailure(''))); - test('angles 3', () => - expect(lang.angles(string('abc')).run(' expect( + lang.natural.chainl(success((x, y) => x + y), 42).run('1 2 3'), + isSuccess(6, ''))); - test('brackets 1', () => - expect(lang.brackets(string('abc')).run('[abc]rest'), - isSuccess('abc', 'rest'))); + test( + 'chainl 2', + () => expect( + lang.natural.chainl(success((x, y) => x + y), 42).run('a 2 3'), + isSuccess(42, 'a 2 3'))); - test('brackets 2', () => - expect(lang.brackets(string('abc')).run('abc]'), isFailure('abc]'))); + final addop = (lang.symbol('+').thenKeep(success((x, y) => x + y))) + .or(lang.symbol('-').thenKeep(success((x, y) => x - y))); - test('brackets 3', () => - expect(lang.brackets(string('abc')).run('[abc'), isFailure(''))); + test( + 'chainl 3', + () => expect( + lang.natural.chainl(addop, 42).run('3 - 1 - 2'), isSuccess(0, ''))); - test('chainl 1', () => - expect(lang.natural.chainl(success((x, y) => x + y), 42).run('1 2 3'), - isSuccess(6, ''))); + test( + 'chainl 4', + () => expect(lang.natural.chainl(addop, 42).run('a - 1 - 2'), + isSuccess(42, 'a - 1 - 2'))); - test('chainl 2', () => - expect(lang.natural.chainl(success((x, y) => x + y), 42).run('a 2 3'), - isSuccess(42, 'a 2 3'))); - - final addop = (lang.symbol('+') > success((x, y) => x + y)) - | (lang.symbol('-') > success((x, y) => x - y)); - - test('chainl 3', () => - expect(lang.natural.chainl(addop, 42).run('3 - 1 - 2'), - isSuccess(0, ''))); - - test('chainl 4', () => - expect(lang.natural.chainl(addop, 42).run('a - 1 - 2'), - isSuccess(42, 'a - 1 - 2'))); - - chainl1Prop1(f) => - expect(f(lang.natural, success((x, y) => x + y)).run('1 2 3'), - isSuccess(6, '')); + chainl1Prop1(f) => expect( + f(lang.natural, success((x, y) => x + y)).run('1 2 3'), isSuccess(6, '')); test('chainl1 1 model', () => chainl1Prop1(chainl1Model)); test('chainl1 1 impl', () => chainl1Prop1(chainl1Impl)); - chainl1Prop2(f) => - expect(f(lang.natural, success((x, y) => x + y)).run('a 2 3'), - isFailure('a 2 3')); + chainl1Prop2(f) => expect( + f(lang.natural, success((x, y) => x + y)).run('a 2 3'), + isFailure('a 2 3')); test('chainl1 2 model', () => chainl1Prop2(chainl1Model)); test('chainl1 2 impl', () => chainl1Prop2(chainl1Impl)); chainl1Prop3(f) => - expect(f(lang.natural, addop).run('3 - 1 - 2'), - isSuccess(0, '')); + expect(f(lang.natural, addop).run('3 - 1 - 2'), isSuccess(0, '')); test('chainl1 3 model', () => chainl1Prop3(chainl1Model)); test('chainl1 3 impl', () => chainl1Prop3(chainl1Impl)); chainl1Prop4(f) => - expect(f(lang.natural, addop).run('a - 1 - 2'), - isFailure('a - 1 - 2')); + expect(f(lang.natural, addop).run('a - 1 - 2'), isFailure('a - 1 - 2')); test('chainl1 4 model', () => chainl1Prop4(chainl1Model)); test('chainl1 4 impl', () => chainl1Prop4(chainl1Impl)); - test('chainr 1', () => - expect(lang.natural.chainr(success((x, y) => x + y), 42).run('1 2 3'), - isSuccess(6, ''))); - - test('chainr 2', () => - expect(lang.natural.chainr(success((x, y) => x + y), 42).run('a 2 3'), - isSuccess(42, 'a 2 3'))); - - test('chainr 3', () => - expect(lang.natural.chainr(addop, 42).run('3 - 1 - 2'), - isSuccess(4, ''))); - - test('chainr 4', () => - expect(lang.intLiteral.chainr(addop, 42).run('a - 1 - 2'), - isSuccess(42, 'a - 1 - 2'))); - - test('chainr1 1', () => - expect(lang.intLiteral.chainr1(success((x, y) => x + y)).run('1 2 3'), - isSuccess(6, ''))); - - test('chainr1 2', () => - expect(lang.intLiteral.chainr1(success((x, y) => x + y)).run('a 2 3'), - isFailure('a 2 3'))); - - test('chainr1 3', () => - expect(lang.intLiteral.chainr1(addop).run('3 - 1 - 2'), - isSuccess(4, ''))); - - test('chainr1 4', () => - expect(lang.intLiteral.chainr1(addop).run('a - 1 - 2'), - isFailure('a - 1 - 2'))); - - test('choice 1', () => - expect(choice([char('a'), char('b'), char('c')]).run('b'), - isSuccess('b', ''))); - - test('choice 2', () => - expect(choice([char('a'), char('b'), char('c')]).run('d'), - isFailure('d'))); - - test('skipEverythingBetween 1', () => - expect(skipEverythingBetween(string('ab'), string('ac')) - .run('ab aaa ab aaa ac aaa ac foo'), - isSuccess(null, ' aaa ac foo'))); - - test('skipEverythingBetween 2', () => - expect(skipEverythingBetween(string('ab'), string('ac'), nested: true) - .run('ab aaa ab aaa ac aaa ac foo'), - isSuccess(null, ' foo'))); - - test('skipEverythingBetween 3', () => - expect(skipEverythingBetween(string('ab'), string('ac')).run("abaaaaa"), - isFailure(''))); - - test('skipEverythingBetween 4', () => - expect(skipEverythingBetween(string('ab'), string('ac'), nested: true) - .run("abaaaaa"), - isFailure(''))); - - test('everythingBetween 1', () => - expect(everythingBetween(string('ab'), string('ac')) - .run('ab aaa ab aaa ac aaa ac foo'), - isSuccess(' aaa ab aaa ', ' aaa ac foo'))); - - test('everythingBetween 2', () => - expect(everythingBetween(string('ab'), string('ac'), nested: true) - .run('ab aaa ab aaa ac aaa ac foo'), - isSuccess(' aaa ab aaa ac aaa ', ' foo'))); - - test('everythingBetween 3', () => - expect(everythingBetween(string('ab'), string('ac')).run("abaaaaa"), - isFailure(''))); - - test('everythingBetween 4', () => - expect(everythingBetween(string('ab'), string('ac'), nested: true) - .run("abaaaaa"), - isFailure(''))); - - test('record 1', () => - expect(char('a').many.record.run('aaaabb'), - isSuccess('aaaa', 'bb'))); - - test('record 2', () => - expect(string('aa').record.run('abaabb'), - isFailure('abaabb'))); - - test('record 2', () => - expect(char('a').record.run(''), - isFailure(''))); - - test('commit 1', () => - expect((char('a').committed | char('b')).run('bc'), - isFailure('bc'))); - - test('commit 2', () => - expect((char('a').committed | char('b')).run('ac'), - isSuccess('a', 'c'))); - - test('commit 3', () => - expect((char('a') > char('b').committed | char('a')).run('acc'), - isFailure('cc'))); - - test('commit 4', () => - expect((char('a') > char('b').committed | char('a')).run('abc'), - isSuccess('b', 'c'))); - - test('commit 5', () => - expect(char('a').committed.many.run('ccc'), - isFailure('ccc'))); - - test('commit 6', () => - expect(char('a').committed.many.run('aac'), - isFailure('c'))); - - test('commit 7', () => - expect(char('a').committed.many.run('aaa'), - isFailure(''))); - - test('commit 8', () => - expect(char('a').committed.many.run(''), - isFailure(''))); - - test('commit 9', () => - expect(char('a').committed.skipMany.run('ccc'), - isFailure('ccc'))); - - test('commit 10', () => - expect(char('a').committed.skipMany.run('aac'), - isFailure('c'))); - - test('commit 11', () => - expect(char('a').committed.skipMany.run('aaa'), - isFailure(''))); - - test('commit 12', () => - expect(char('a').committed.skipMany.run(''), - isFailure(''))); + test( + 'chainr 1', + () => expect( + lang.natural.chainr(success((x, y) => x + y), 42).run('1 2 3'), + isSuccess(6, ''))); + + test( + 'chainr 2', + () => expect( + lang.natural.chainr(success((x, y) => x + y), 42).run('a 2 3'), + isSuccess(42, 'a 2 3'))); + + test( + 'chainr 3', + () => expect( + lang.natural.chainr(addop, 42).run('3 - 1 - 2'), isSuccess(4, ''))); + + test( + 'chainr 4', + () => expect(lang.intLiteral.chainr(addop, 42).run('a - 1 - 2'), + isSuccess(42, 'a - 1 - 2'))); + + test( + 'chainr1 1', + () => expect( + lang.intLiteral.chainr1(success((x, y) => x + y)).run('1 2 3'), + isSuccess(6, ''))); + + test( + 'chainr1 2', + () => expect( + lang.intLiteral.chainr1(success((x, y) => x + y)).run('a 2 3'), + isFailure('a 2 3'))); + + test( + 'chainr1 3', + () => expect( + lang.intLiteral.chainr1(addop).run('3 - 1 - 2'), isSuccess(4, ''))); + + test( + 'chainr1 4', + () => expect(lang.intLiteral.chainr1(addop).run('a - 1 - 2'), + isFailure('a - 1 - 2'))); + + test( + 'choice 1', + () => expect(choice([char('a'), char('b'), char('c')]).run('b'), + isSuccess('b', ''))); + + test( + 'choice 2', + () => expect( + choice([char('a'), char('b'), char('c')]).run('d'), isFailure('d'))); + + test( + 'skipEverythingBetween 1', + () => expect( + skipEverythingBetween(string('ab'), string('ac')) + .run('ab aaa ab aaa ac aaa ac foo'), + isSuccess(null, ' aaa ac foo'))); + + test( + 'skipEverythingBetween 2', + () => expect( + skipEverythingBetween(string('ab'), string('ac'), nested: true) + .run('ab aaa ab aaa ac aaa ac foo'), + isSuccess(null, ' foo'))); + + test( + 'skipEverythingBetween 3', + () => expect( + skipEverythingBetween(string('ab'), string('ac')).run('abaaaaa'), + isFailure(''))); + + test( + 'skipEverythingBetween 4', + () => expect( + skipEverythingBetween(string('ab'), string('ac'), nested: true) + .run('abaaaaa'), + isFailure(''))); + + test( + 'everythingBetween 1', + () => expect( + everythingBetween(string('ab'), string('ac')) + .run('ab aaa ab aaa ac aaa ac foo'), + isSuccess(' aaa ab aaa ', ' aaa ac foo'))); + + test( + 'everythingBetween 2', + () => expect( + everythingBetween(string('ab'), string('ac'), nested: true) + .run('ab aaa ab aaa ac aaa ac foo'), + isSuccess(' aaa ab aaa ac aaa ', ' foo'))); + + test( + 'everythingBetween 3', + () => expect(everythingBetween(string('ab'), string('ac')).run('abaaaaa'), + isFailure(''))); + + test( + 'everythingBetween 4', + () => expect( + everythingBetween(string('ab'), string('ac'), nested: true) + .run('abaaaaa'), + isFailure(''))); + + test( + 'record 1', + () => + expect(char('a').many.record.run('aaaabb'), isSuccess('aaaa', 'bb'))); + + test('record 2', + () => expect(string('aa').record.run('abaabb'), isFailure('abaabb'))); + + test('record 2', () => expect(char('a').record.run(''), isFailure(''))); + + test( + 'commit 1', + () => expect( + (char('a').committed.or(char('b'))).run('bc'), isFailure('bc'))); + + test( + 'commit 2', + () => expect( + (char('a').committed.or(char('b'))).run('ac'), isSuccess('a', 'c'))); + + test( + 'commit 3', + () => expect( + (char('a').thenKeep(char('b').committed).or(char('a'))).run('acc'), + isFailure('cc'))); + + test( + 'commit 4', + () => expect( + (char('a').thenKeep(char('b').committed).or(char('a'))).run('abc'), + isSuccess('b', 'c'))); + + test('commit 5', + () => expect(char('a').committed.many.run('ccc'), isFailure('ccc'))); + + test('commit 6', + () => expect(char('a').committed.many.run('aac'), isFailure('c'))); + + test('commit 7', + () => expect(char('a').committed.many.run('aaa'), isFailure(''))); + + test('commit 8', + () => expect(char('a').committed.many.run(''), isFailure(''))); + + test('commit 9', + () => expect(char('a').committed.skipMany.run('ccc'), isFailure('ccc'))); + + test('commit 10', + () => expect(char('a').committed.skipMany.run('aac'), isFailure('c'))); + + test('commit 11', + () => expect(char('a').committed.skipMany.run('aaa'), isFailure(''))); + + test('commit 12', + () => expect(char('a').committed.skipMany.run(''), isFailure(''))); plus(x, y) => x + y; - test('commit 13', () => - expect(lang.natural.committed.chainl(success(plus), 42).run('1 2 3'), - isFailure(''))); + test( + 'commit 13', + () => expect( + lang.natural.committed.chainl(success(plus), 42).run('1 2 3'), + isFailure(''))); - commit135(f) => - expect(f(lang.natural.committed, success(plus)).run('1 2 3'), - isFailure('')); + commit135(f) => expect( + f(lang.natural.committed, success(plus)).run('1 2 3'), isFailure('')); test('commit 13.5 model', () => commit135(chainl1Model)); test('commit 13.5 model', () => commit135(chainl1Impl)); - test('commit 14', () => - expect(lang.natural.committed.chainl(success(plus), 42).run('a 2 3'), - isFailure('a 2 3'))); + test( + 'commit 14', + () => expect( + lang.natural.committed.chainl(success(plus), 42).run('a 2 3'), + isFailure('a 2 3'))); - commit145(f) => - expect(f(lang.natural.committed, success(plus)).run('a 2 3'), - isFailure('a 2 3')); + commit145(f) => expect(f(lang.natural.committed, success(plus)).run('a 2 3'), + isFailure('a 2 3')); test('commit 14.5 model', () => commit145(chainl1Model)); test('commit 14.5 model', () => commit145(chainl1Impl)); - test('commit 15', () => - expect(lang.natural.chainl(success(plus).committed, 42).run('1 2 3'), - isFailure(''))); + test( + 'commit 15', + () => expect( + lang.natural.chainl(success(plus).committed, 42).run('1 2 3'), + isFailure(''))); - commit155(f) => - expect(f(lang.natural, success(plus).committed).run('1 2 3'), - isFailure('')); + commit155(f) => expect( + f(lang.natural, success(plus).committed).run('1 2 3'), isFailure('')); test('commit 15.5 model', () => commit155(chainl1Model)); test('commit 15.5 model', () => commit155(chainl1Impl)); - test('commit 16', () => - expect(lang.natural.chainl(success(plus).committed, 42).run('a 2 3'), - isSuccess(42, 'a 2 3'))); + test( + 'commit 16', + () => expect( + lang.natural.chainl(success(plus).committed, 42).run('a 2 3'), + isSuccess(42, 'a 2 3'))); - commit165(f) => - expect(f(lang.natural, success(plus).committed).run('a 2 3'), - isFailure('a 2 3')); + commit165(f) => expect(f(lang.natural, success(plus).committed).run('a 2 3'), + isFailure('a 2 3')); test('commit 16.5 model', () => commit165(chainl1Model)); test('commit 16.5 model', () => commit165(chainl1Impl)); - test('commit 17', () => - expect(choice([char('a').committed, char('b'), char('c')]).run('az'), - isSuccess('a', 'z'))); - - test('commit 18', () => - expect(choice([char('a').committed, char('b'), char('c')]).run('bz'), - isFailure('bz'))); - - test('commit 19', () => - expect(choice([char('a'), char('b').committed, char('c')]).run('bz'), - isSuccess('b', 'z'))); - - test('commit 20', () => - expect(choice([char('a'), char('b').committed, char('c')]).run('cz'), - isFailure('cz'))); + test( + 'commit 17', + () => expect( + choice([char('a').committed, char('b'), char('c')]).run('az'), + isSuccess('a', 'z'))); + + test( + 'commit 18', + () => expect( + choice([char('a').committed, char('b'), char('c')]).run('bz'), + isFailure('bz'))); + + test( + 'commit 19', + () => expect( + choice([char('a'), char('b').committed, char('c')]).run('bz'), + isSuccess('b', 'z'))); + + test( + 'commit 20', + () => expect( + choice([char('a'), char('b').committed, char('c')]).run('cz'), + isFailure('cz'))); commit21Prop(f) => - expect(f(char('a').committed, char('z')).run('ccc'), - isFailure('ccc')); + expect(f(char('a').committed, char('z')).run('ccc'), isFailure('ccc')); test('commit 21 model', () => commit21Prop(skipManyUntilModel)); test('commit 21 impl', () => commit21Prop(skipManyUntilImpl)); commit22Prop(f) => - expect(f(char('a').committed, char('z')).run('aac'), - isFailure('c')); + expect(f(char('a').committed, char('z')).run('aac'), isFailure('c')); test('commit 22 model', () => commit22Prop(skipManyUntilModel)); test('commit 22 impl', () => commit22Prop(skipManyUntilImpl)); commit23Prop(f) => - expect(f(char('a').committed, char('z')).run('aaa'), - isFailure('')); + expect(f(char('a').committed, char('z')).run('aaa'), isFailure('')); test('commit 23 model', () => commit23Prop(skipManyUntilModel)); test('commit 23 impl', () => commit23Prop(skipManyUntilImpl)); commit24Prop(f) => - expect(f(char('a').committed, char('z')).run(''), - isFailure('')); + expect(f(char('a').committed, char('z')).run(''), isFailure('')); test('commit 24 model', () => commit24Prop(skipManyUntilModel)); test('commit 24 impl', () => commit24Prop(skipManyUntilImpl)); - commit25Prop(f) => - expect(f(char('a').committed, char('z')).run('aaazw'), - isSuccess(null, 'w')); + commit25Prop(f) => expect( + f(char('a').committed, char('z')).run('aaazw'), isSuccess(null, 'w')); test('commit 25 model', () => commit25Prop(skipManyUntilModel)); test('commit 25 impl', () => commit25Prop(skipManyUntilImpl)); - commit26Prop(f) => - expect(f(char('a'), char('z').committed).run('aaazw'), - isFailure('aaazw')); + commit26Prop(f) => expect( + f(char('a'), char('z').committed).run('aaazw'), isFailure('aaazw')); test('commit 26 model', () => commit26Prop(skipManyUntilModel)); test('commit 26 impl', () => commit26Prop(skipManyUntilImpl)); commit27Prop(f) => - expect(f(char('a'), char('z').committed).run('zw'), - isSuccess(null, 'w')); + expect(f(char('a'), char('z').committed).run('zw'), isSuccess(null, 'w')); test('commit 27 model', () => commit27Prop(skipManyUntilModel)); test('commit 27 impl', () => commit27Prop(skipManyUntilImpl)); commit28Prop(f) => - expect(f(char('a').committed, char('z')).run('ccc'), - isFailure('ccc')); + expect(f(char('a').committed, char('z')).run('ccc'), isFailure('ccc')); test('commit 28 model', () => commit28Prop(manyUntilModel)); test('commit 28 impl', () => commit28Prop(manyUntilImpl)); commit29Prop(f) => - expect(f(char('a').committed, char('z')).run('aac'), - isFailure('c')); + expect(f(char('a').committed, char('z')).run('aac'), isFailure('c')); test('commit 29 model', () => commit29Prop(manyUntilModel)); test('commit 29 impl', () => commit29Prop(manyUntilImpl)); commit30Prop(f) => - expect(f(char('a').committed, char('z')).run('aaa'), - isFailure('')); + expect(f(char('a').committed, char('z')).run('aaa'), isFailure('')); test('commit 30 model', () => commit30Prop(manyUntilModel)); test('commit 30 impl', () => commit30Prop(manyUntilImpl)); commit31Prop(f) => - expect(f(char('a').committed, char('z')).run(''), - isFailure('')); + expect(f(char('a').committed, char('z')).run(''), isFailure('')); test('commit 31 model', () => commit31Prop(manyUntilModel)); test('commit 31 impl', () => commit31Prop(manyUntilImpl)); - - commit32Prop(f) => - expect(f(char('a').committed, char('z')).run('aaazw'), - isSuccess(['a','a','a'], 'w')); + commit32Prop(f) => expect(f(char('a').committed, char('z')).run('aaazw'), + isSuccess(['a', 'a', 'a'], 'w')); test('commit 32 model', () => commit32Prop(manyUntilModel)); test('commit 32 impl', () => commit32Prop(manyUntilImpl)); - commit33Prop(f) => - expect(f(char('a'), char('z').committed).run('aaazw'), - isFailure('aaazw')); + commit33Prop(f) => expect( + f(char('a'), char('z').committed).run('aaazw'), isFailure('aaazw')); test('commit 33 model', () => commit33Prop(manyUntilModel)); test('commit 33 impl', () => commit33Prop(manyUntilImpl)); commit34Prop(f) => - expect(f(char('a'), char('z').committed).run('zw'), - isSuccess([], 'w')); + expect(f(char('a'), char('z').committed).run('zw'), isSuccess([], 'w')); test('commit 34 model', () => commit34Prop(manyUntilModel)); test('commit 34 impl', () => commit34Prop(manyUntilImpl)); - - - commit35Prop(f) => - expect(f(char('a').committed).run('ccc'), - isFailure('ccc')); + expect(f(char('a').committed).run('ccc'), isFailure('ccc')); test('commit 35 model', () => commit35Prop(manyModel)); test('commit 35 impl', () => commit35Prop(manyImpl)); - commit36Prop(f) => - expect(f(char('a').committed).run('aac'), - isFailure('c')); + commit36Prop(f) => expect(f(char('a').committed).run('aac'), isFailure('c')); test('commit 36 model', () => commit36Prop(manyModel)); test('commit 36 impl', () => commit36Prop(manyImpl)); - commit37Prop(f) => - expect(f(char('a').committed).run('aaa'), - isFailure('')); + commit37Prop(f) => expect(f(char('a').committed).run('aaa'), isFailure('')); test('commit 37 model', () => commit37Prop(manyModel)); test('commit 37 impl', () => commit37Prop(manyImpl)); - commit38Prop(f) => - expect(f(char('a').committed).run(''), - isFailure('')); + commit38Prop(f) => expect(f(char('a').committed).run(''), isFailure('')); test('commit 38 model', () => commit38Prop(manyModel)); test('commit 38 impl', () => commit38Prop(manyImpl)); commit39Prop(f) => - expect(f(char('a').committed).run('aaazw'), - isFailure('zw')); + expect(f(char('a').committed).run('aaazw'), isFailure('zw')); test('commit 39 model', () => commit39Prop(manyModel)); test('commit 39 impl', () => commit39Prop(manyImpl)); commit40Prop(f) => - expect(f(char('a').committed).run('ccc'), - isFailure('ccc')); + expect(f(char('a').committed).run('ccc'), isFailure('ccc')); test('commit 40 model', () => commit40Prop(skipManyModel)); test('commit 40 impl', () => commit40Prop(skipManyImpl)); - commit41Prop(f) => - expect(f(char('a').committed).run('aac'), - isFailure('c')); + commit41Prop(f) => expect(f(char('a').committed).run('aac'), isFailure('c')); test('commit 41 model', () => commit41Prop(skipManyModel)); test('commit 41 impl', () => commit41Prop(skipManyImpl)); - commit42Prop(f) => - expect(f(char('a').committed).run('aaa'), - isFailure('')); + commit42Prop(f) => expect(f(char('a').committed).run('aaa'), isFailure('')); test('commit 42 model', () => commit42Prop(skipManyModel)); test('commit 42 impl', () => commit42Prop(skipManyImpl)); - commit43Prop(f) => - expect(f(char('a').committed).run(''), - isFailure('')); + commit43Prop(f) => expect(f(char('a').committed).run(''), isFailure('')); test('commit 43 model', () => commit43Prop(skipManyModel)); test('commit 43 impl', () => commit43Prop(skipManyImpl)); commit44Prop(f) => - expect(f(char('a').committed).run('aaazw'), - isFailure('zw')); + expect(f(char('a').committed).run('aaazw'), isFailure('zw')); test('commit 44 model', () => commit44Prop(skipManyModel)); test('commit 44 impl', () => commit44Prop(skipManyImpl)); commit45Prop(f) { t3(x) => (y) => (z) => '$x$y$z'; - final p = (success(t3) * char('a') * char('b').committed * char('c')) - | (success(t3) * char('d') * char('e') * char('f')); - return expect(f(p).run('abcabczz'), - isSuccess(['abc', 'abc'], 'zz')); + final p = (success(t3) + .apply(char('a')) + .apply(char('b').committed) + .apply(char('c'))) + .or(success(t3).apply(char('d')).apply(char('e')).apply(char('f'))); + return expect(f(p).run('abcabczz'), isSuccess(['abc', 'abc'], 'zz')); } test('commit 45 model', () => commit45Prop(manyModel)); @@ -936,10 +968,13 @@ main() { commit46Prop(f) { t3(x) => (y) => (z) => '$x$y$z'; - final p = (success(t3) * char('a') * char('b').committed * char('c')) - | (success(t3) * char('d') * char('e') * char('f')); - return expect(f(p).run('abcdefabczz'), - isSuccess(['abc', 'def', 'abc'], 'zz')); + final p = (success(t3) + .apply(char('a')) + .apply(char('b').committed) + .apply(char('c'))) + .or(success(t3).apply(char('d')).apply(char('e')).apply(char('f'))); + return expect( + f(p).run('abcdefabczz'), isSuccess(['abc', 'def', 'abc'], 'zz')); } test('commit 46 model', () => commit46Prop(manyModel)); @@ -947,18 +982,21 @@ main() { commit47Prop(f) { t3(x) => (y) => (z) => '$x$y$z'; - final p = (success(t3) * char('a') * char('b').committed * char('c')) - | (success(t3) * char('a') * char('e') * char('f')); - return expect(f(p).run('abcaefzz'), - isFailure('efzz')); + final p = (success(t3) + .apply(char('a')) + .apply(char('b').committed) + .apply(char('c'))) + .or(success(t3).apply(char('a')).apply(char('e')).apply(char('f'))); + return expect(f(p).run('abcaefzz'), isFailure('efzz')); } test('commit 47 model', () => commit47Prop(manyModel)); test('commit 47 impl', () => commit47Prop(manyImpl)); commit475Prop(f) { - final p = f(char('x') > char('a').committed) > string('b') - | string('xaxac'); + final p = f(char('x').thenKeep(char('a').committed)) + .thenKeep(string('b')) + .or(string('xaxac')); return expect(p.run('xaxac'), isFailure('c')); } @@ -966,8 +1004,8 @@ main() { test('commit 47.5 impl', () => commit475Prop(manyImpl)); commit48Prop(f) { - final p = (char('a') > (char('b').committed > char('c'))) - | (char('d') > (char('e') > char('f'))); + final p = (char('a').thenKeep(char('b').committed.thenKeep(char('c')))) + .or(char('d').thenKeep(char('e').thenKeep(char('f')))); return expect(f(p).run('abcabczz'), isSuccess(null, 'zz')); } @@ -975,8 +1013,8 @@ main() { test('commit 48 impl', () => commit48Prop(skipManyImpl)); commit49Prop(f) { - final p = (char('a') > (char('b').committed > char('c'))) - | (char('d') > (char('e') > char('f'))); + final p = (char('a').thenKeep(char('b').committed.thenKeep(char('c')))) + .or(char('d').thenKeep(char('e').thenKeep(char('f')))); return expect(f(p).run('abcdefabczz'), isSuccess(null, 'zz')); } @@ -984,8 +1022,8 @@ main() { test('commit 49 impl', () => commit49Prop(skipManyImpl)); commit50Prop(f) { - final p = (char('a') > (char('b').committed > char('c'))) - | (char('d') > (char('e') > char('f'))); + final p = (char('a').thenKeep(char('b').committed.thenKeep(char('c')))) + .or(char('d').thenKeep(char('e').thenKeep(char('f')))); return expect(f(p).run('abcaefzz'), isFailure('efzz')); } @@ -994,18 +1032,21 @@ main() { commit51Prop(f) { t3(x) => (y) => (z) => '$x$y$z'; - final p = (success(t3) * char('a') * char('b').committed * char('c')) - | (success(t3) * char('a') * char('e') * char('f')); - return expect(f(p, char('z')).run('abcabczz'), - isSuccess(['abc', 'abc'], 'z')); + final p = (success(t3) + .apply(char('a')) + .apply(char('b').committed) + .apply(char('c'))) + .or(success(t3).apply(char('a')).apply(char('e')).apply(char('f'))); + return expect( + f(p, char('z')).run('abcabczz'), isSuccess(['abc', 'abc'], 'z')); } test('commit 51 model', () => commit51Prop(manyUntilModel)); test('commit 51 impl', () => commit51Prop(manyUntilImpl)); commit515Prop(f) { - final p = (f(char('x') > char('a').committed) > string('b')) - | string('xaxac'); + final p = (f(char('x').thenKeep(char('a').committed)).thenKeep(string('b'))) + .or(string('xaxac')); return expect(p.run('xaxac'), isFailure('c')); } @@ -1014,10 +1055,13 @@ main() { commit52Prop(f) { t3(x) => (y) => (z) => '$x$y$z'; - final p = (success(t3) * char('a') * char('b').committed * char('c')) - | (success(t3) * char('d') * char('e') * char('f')); + final p = (success(t3) + .apply(char('a')) + .apply(char('b').committed) + .apply(char('c'))) + .or(success(t3).apply(char('d')).apply(char('e')).apply(char('f'))); return expect(f(p, char('z')).run('abcdefabczz'), - isSuccess(['abc', 'def', 'abc'], 'z')); + isSuccess(['abc', 'def', 'abc'], 'z')); } test('commit 52 model', () => commit52Prop(manyUntilModel)); @@ -1025,8 +1069,11 @@ main() { commit53Prop(f) { t3(x) => (y) => (z) => '$x$y$z'; - final p = (success(t3) * char('a') * char('b').committed * char('c')) - | (success(t3) * char('a') * char('e') * char('f')); + final p = (success(t3) + .apply(char('a')) + .apply(char('b').committed) + .apply(char('c'))) + .or(success(t3).apply(char('a')).apply(char('e')).apply(char('f'))); return expect(f(p, char('z')).run('abcaefzz'), isFailure('efzz')); } @@ -1034,8 +1081,9 @@ main() { test('commit 53 impl', () => commit53Prop(manyUntilModel)); commit54Prop(f) { - final p = f(char('x') > char('a').committed, char('z')) > string('b') - | string('xaxazc'); + final p = f(char('x').thenKeep(char('a').committed), char('z')) + .thenKeep(string('b')) + .or(string('xaxazc')); return expect(p.run('xaxazc'), isFailure('c')); } @@ -1043,37 +1091,43 @@ main() { test('commit 54 impl', () => commit54Prop(manyUntilImpl)); commit55Prop(f) { - final p = f(char('x') > char('a').committed, char('z')) - | string('xaxazc'); + final p = f(char('x').thenKeep(char('a').committed), char('z')) + .or(string('xaxazc')); return expect(p.run('xaxaw'), isFailure('w')); } test('commit 55 model', () => commit55Prop(manyUntilModel)); test('commit 55 impl', () => commit55Prop(manyUntilImpl)); - - - - commit56Prop(f) { - t3(x) => (y) => (z) => int.parse(x) + int.parse(y) + int.parse(z); + t3(x) => (y) => (z) => + int.parse(x as String) + + int.parse(y as String) + + int.parse(z as String); plus(x, y) => x + y; - final p = (success(t3) * char('1') * char('2').committed * char('3')) - | (success(t3) * char('4') * char('5') * char('6')); - return expect(f(p, success(plus)).run('123456123zz'), - isSuccess(27, 'zz')); + final p = (success(t3) + .apply(char('1')) + .apply(char('2').committed) + .apply(char('3'))) + .or(success(t3).apply(char('4')).apply(char('5')).apply(char('6'))); + return expect(f(p, success(plus)).run('123456123zz'), isSuccess(27, 'zz')); } test('commit 56 model', () => commit56Prop(chainl1Model)); test('commit 56 impl', () => commit56Prop(chainl1Impl)); commit57Prop(f) { - t3(x) => (y) => (z) => int.parse(x) + int.parse(y) + int.parse(z); + t3(x) => (y) => (z) => + int.parse(x as String) + + int.parse(y as String) + + int.parse(z as String); plus(x, y) => x + y; - final p = (success(t3) * char('1') * char('2').committed * char('3')) - | (success(t3) * char('1') * char('5') * char('6')); - return expect(f(p, success(plus)).run('123156zz'), - isFailure('56zz')); + final p = (success(t3) + .apply(char('1')) + .apply(char('2').committed) + .apply(char('3'))) + .or(success(t3).apply(char('1')).apply(char('5')).apply(char('6'))); + return expect(f(p, success(plus)).run('123156zz'), isFailure('56zz')); } test('commit 57 model', () => commit57Prop(chainl1Model)); @@ -1081,8 +1135,9 @@ main() { commit58Prop(f) { plus(x, y) => '$x$y'; - final p = f(char('x') > char('a').committed, success(plus)) > string('b') - | string('xaxac'); + final p = f(char('x').thenKeep(char('a').committed), success(plus)) + .thenKeep(string('b')) + .or(string('xaxac')); return expect(p.run('xaxac'), isFailure('c')); } @@ -1091,43 +1146,51 @@ main() { commit59Prop(f) { plus(x, y) => '$x$y'; - final p = f(char('x') > char('a'), success(plus).committed) > string('b') - | string('xaxac'); + final p = f(char('x').thenKeep(char('a')), success(plus).committed) + .thenKeep(string('b')) + .or(string('xaxac')); return expect(p.run('xaxac'), isFailure('c')); } test('commit 59 model', () => commit59Prop(chainl1Model)); test('commit 59 impl', () => commit59Prop(chainl1Impl)); - test('sequence map 1', () => - expect((char('a') + char('b') ^ (x,y) => '$y$x').run('abc'), - isSuccess('ba', 'c'))); - - test('sequence map 2', () => - expect((char('a') + char('b') ^ (x,y) => '$y$x').run('acb'), - isFailure('cb'))); - - test('sequence list 1', () => - expect((char('a') + char('b')).list.run('abc'), - isSuccess(['a', 'b'], 'c'))); - - test('sequence list 2', () => - expect((char('a') + char('b')).list.run('acb'), - isFailure('cb'))); - - var big = "a"; - for (int i = 0; i < 15; i++) { big = '$big$big'; } + test( + 'sequence map 1', + () => expect((char('a').and(char('b')).map((x, y) => '$y$x')).run('abc'), + isSuccess('ba', 'c'))); + + test( + 'sequence map 2', + () => expect((char('a').and(char('b')).map((x, y) => '$y$x')).run('acb'), + isFailure('cb'))); + + test( + 'sequence list 1', + () => expect((char('a').and(char('b'))).list.run('abc'), + isSuccess(['a', 'b'], 'c'))); + + test( + 'sequence list 2', + () => + expect((char('a').and(char('b'))).list.run('acb'), isFailure('cb'))); + + var big = 'a'; + for (int i = 0; i < 15; i++) { + big = '$big$big'; + } - test('no stack overflow many', () => - expect(char('a').many.run(big).value.length, equals(32768))); + test('no stack overflow many', + () => expect(char('a').many.run(big).value.length, equals(32768))); - test('no stack overflow skipMany', () => - expect(char('a').skipMany.run('${big}bb'), isSuccess(null, 'bb'))); + test('no stack overflow skipMany', + () => expect(char('a').skipMany.run('${big}bb'), isSuccess(null, 'bb'))); - test('no stack overflow manyUntil', () => - expect(anyChar.manyUntil(char('b')).run('${big}b').value.length, - equals(32768))); + test( + 'no stack overflow manyUntil', + () => expect(anyChar.manyUntil(char('b')).run('${big}b').value.length, + equals(32768))); - test('no stack overflow comment', () => - expect(lang.natural.run('1 /* $big */'), isSuccess(1, ''))); + test('no stack overflow comment', + () => expect(lang.natural.run('1 /* $big */'), isSuccess(1, ''))); } diff --git a/test/src/parsers_model.dart b/test/src/parsers_model.dart index 3a5641c..bbabf96 100644 --- a/test/src/parsers_model.dart +++ b/test/src/parsers_model.dart @@ -3,39 +3,46 @@ part of parsers_test; // Simple models of the most complex combinators (the one that use while loops // to avoid stack overflows). -_cons(x) => (xs) => []..add(x)..addAll(xs); +List Function(dynamic) _cons(x) { + return (xs) { + return [] + ..add(x) + ..addAll(xs as Iterable); + }; +} Parser manyModel(Parser p) { - go () => success(_cons) * p * rec(go) | success([]); + Parser go() => success(_cons).apply(p).apply(rec(go)).or(success([])); return go(); } Parser manyImpl(Parser p) => p.many; -Parser skipManyModel(Parser p) => manyModel(p) > success(null); +Parser skipManyModel(Parser p) => manyModel(p).thenKeep(success(null)); Parser skipManyImpl(Parser p) => p.skipMany; Parser manyUntilModel(Parser p, Parser end) { - go () => (end > success([])) - | success(_cons) * p * rec(go); + Parser go() => (end.thenKeep(success([]))) + .or(success(_cons).apply(p).apply(rec(go))); return go(); } Parser manyUntilImpl(Parser p, Parser end) => p.manyUntil(end); -Parser skipManyUntilModel(Parser p, Parser end) { - return manyUntilModel(p, end) > success(null); +Parser skipManyUntilModel(Parser p, Parser end) { + return manyUntilModel(p, end).thenKeep(success(null)); } Parser skipManyUntilImpl(Parser p, Parser end) => p.skipManyUntil(end); Parser chainl1Model(Parser p, Parser sep) { - rest(acc) { + Parser rest(acc) { combine(f) => (x) => f(acc, x); - return (success(combine) * sep * p) >> rest | success(acc); + return (success(combine).apply(sep).apply(p)).then(rest).or(success(acc)); } - return p >> rest; + + return p.then(rest); } -Parser chainl1Impl(Parser p, Parser sep) => p.chainl1(sep); +Parser chainl1Impl(Parser p, Parser sep) => p.chainl1(sep as Parser); diff --git a/tool/gen_accumulators.dart b/tool/gen_accumulators.dart index f8d9de6..31882f8 100755 --- a/tool/gen_accumulators.dart +++ b/tool/gen_accumulators.dart @@ -1,5 +1,4 @@ #!/usr/bin/env dart - // Copyright (c) 2012, Google Inc. All rights reserved. Use of this source code // is governed by a BSD-style license that can be found in the LICENSE file. @@ -20,8 +19,8 @@ main(List arguments) { tlist.add('T$j'); typedxlist.add('T$j x$j'); } - final newt = 'T${i+1}'; - final newtlist = new List.from(tlist)..add(newt); + final newt = 'T${i + 1}'; + final newtlist = List.from(tlist)..add(newt); final ts = tlist.join(', '); final newts = newtlist.join(', '); final ps = plist.join(', '); @@ -32,24 +31,24 @@ main(List arguments) { final curriedXs = typedxlist.map((x) => '($x)').join(' => '); final psProduct = plist.map((p) => '.apply($p)').join(''); - print(''' + print(''' class ParserAccumulator${i}<${ts}> { $pdecls ParserAccumulator$i($these); '''); - if (i < n) { - print(''' + if (i < n) { + print(''' /// Parser sequencing: creates a parser accumulator - ParserAccumulator${i+1}<${newts}> and<${newt}>(Parser<${newt}> p) => - new ParserAccumulator${i+1}($ps, p); + ParserAccumulator${i + 1}<${newts}> and<${newt}>(Parser<${newt}> p) => + new ParserAccumulator${i + 1}($ps, p); /// Alias for [and] - ParserAccumulator${i+1} operator +(Parser p) => and(p); + ParserAccumulator${i + 1} operator +(Parser p) => and(p); '''); - } + } - print(''' + print(''' /// Action application Parser map(R f($typedxs)) => success($curriedXs => f($xs))$psProduct;