Linter Demo Errors: 51Warnings: 23File: /home/fstrocco/Dart/dart/benchmark/petitparser/lib/petitparser.dart library ppp.petitparser; class ActionParser extends DelegateParser { final Function _function; ActionParser(parser, this._function) : super(parser); @override Result parseOn(Context context) { var result = _delegate.parseOn(context); if (result.isSuccess) { return result.success(_function(result.value)); } else { return result; } } @override Parser copy() => new ActionParser(_delegate, _function); @override bool hasEqualProperties(Parser other) { return other is ActionParser && super.hasEqualProperties(other) && _function == other._function; } } class TrimmingParser extends DelegateParser { Parser _left; Parser _right; TrimmingParser(parser, this._left, this._right) : super(parser); @override Result parseOn(Context context) { var current = context; do { current = _left.parseOn(current); } while (current.isSuccess); var result = _delegate.parseOn(current); if (result.isFailure) { return result; } current = result; do { current = _right.parseOn(current); } while (current.isSuccess); return current.success(result.value); } @override Parser copy() => new TrimmingParser(_delegate, _left, _right); @override List get children => [_delegate, _left, _right]; @override void replace(Parser source, Parser target) { super.replace(source, target); if (_left == source) { _left = target; } if (_right == source) { _right = target; } } } class FlattenParser extends DelegateParser { FlattenParser(parser) : super(parser); @override Result parseOn(Context context) { var result = _delegate.parseOn(context); if (result.isSuccess) { var output = context.buffer is String ? context.buffer.substring(context.position, result.position) : context.buffer.sublist(context.position, result.position); return result.success(output); } else { return result; } } @override Parser copy() => new FlattenParser(_delegate); } class TokenParser extends DelegateParser { TokenParser(parser) : super(parser); @override Result parseOn(Context context) { var result = _delegate.parseOn(context); if (result.isSuccess) { var token = new Token(result.value, context.buffer, context.position, result.position); return result.success(token); } else { return result; } } @override Parser copy() => new TokenParser(_delegate); } class CharacterParser extends Parser { final CharacterPredicate _predicate; final String _message; CharacterParser(this._predicate, this._message); @override Result parseOn(Context context) { var buffer = context.buffer; var position = context.position; if (position < buffer.length && _predicate.test(buffer.codeUnitAt(position))) { return context.success(buffer[position], position + 1); } return context.failure(_message); } @override String toString() => '${super.toString()}[$_message]'; @override Parser copy() => new CharacterParser(_predicate, _message); @override bool hasEqualProperties(Parser other) { return other is CharacterParser && super.hasEqualProperties(other) && _predicate == other._predicate && _message == other._message; } } abstract class CharacterPredicate { bool test(int value); } class _NotCharacterPredicate implements CharacterPredicate { final CharacterPredicate predicate; _NotCharacterPredicate(this.predicate); @override bool test(int value) => !predicate.test(value); } Parser anyOf(String string, [String message]) { return new CharacterParser(_optimizedString(string), message != null ? message : 'any of "$string" expected'); } CharacterPredicate _optimizedString(String string) { var ranges = string.codeUnits.map((value) => new _RangeCharPredicate(value, value)); return _optimizedRanges(ranges); } CharacterPredicate _optimizedRanges(Iterable<_RangeCharPredicate> ranges) { var sortedRanges = new List.from(ranges, growable: false); sortedRanges.sort((first, second) { return first.start != second.start ? first.start - second.start : first.stop - second.stop; }); var mergedRanges = new List(); for (var thisRange in sortedRanges) { if (mergedRanges.isEmpty) { mergedRanges.add(thisRange); } else { var lastRange = mergedRanges.last; if (lastRange.stop + 1 >= thisRange.start) { var characterRange = new _RangeCharPredicate(lastRange.start, thisRange.stop); mergedRanges[mergedRanges.length - 1] = characterRange; } else { mergedRanges.add(thisRange); } } } if (mergedRanges.length == 1) { return mergedRanges[0].start == mergedRanges[0].stop ? new _SingleCharPredicate(mergedRanges[0].start) : mergedRanges[0]; } else { return new _RangesCharPredicate(mergedRanges.length, mergedRanges.map((range) => range.start).toList(growable: false), mergedRanges.map((range) => range.stop).toList(growable: false)); } } Parser noneOf(String string, [String message]) { return new CharacterParser(new _NotCharacterPredicate(_optimizedString(string)), message != null ? message : 'none of "$string" expected'); } Parser char(element, [String message]) { return new CharacterParser(new _SingleCharPredicate(_toCharCode(element)), message != null ? message : '"$element" expected'); } class _SingleCharPredicate implements CharacterPredicate { final int value; const _SingleCharPredicate(this.value); @override bool test(int value) => identical(this.value, value); } Parser digit([String message]) { return new CharacterParser(_digitCharPredicate, message != null ? message : 'digit expected'); } class _DigitCharPredicate implements CharacterPredicate { const _DigitCharPredicate(); @override bool test(int value) => 48 <= value && value <= 57; } const _digitCharPredicate = const _DigitCharPredicate(); Parser letter([String message]) { return new CharacterParser(_letterCharPredicate, message != null ? message : 'letter expected'); } class _LetterCharPredicate implements CharacterPredicate { const _LetterCharPredicate(); @override bool test(int value) => (65 <= value && value <= 90) || (97 <= value && value <= 122); } const _letterCharPredicate = const _LetterCharPredicate(); Parser lowercase([String message]) { return new CharacterParser(_lowercaseCharPredicate, message != null ? message : 'lowercase letter expected'); } class _LowercaseCharPredicate implements CharacterPredicate { const _LowercaseCharPredicate(); @override bool test(int value) => 97 <= value && value <= 122; } const _lowercaseCharPredicate = const _LowercaseCharPredicate(); Parser pattern(String element, [String message]) { return new CharacterParser(_patternParser.parse(element).value, message != null ? message : '[$element] expected'); } Parser _createPatternParser() { var single = any().map((each) => new _RangeCharPredicate(_toCharCode(each), _toCharCode(each))); var multiple = any().seq(char('-')).seq(any()).map((each) => new _RangeCharPredicate(_toCharCode(each[0]), _toCharCode(each[2]))); var positive = multiple.or(single).plus().map((each) => _optimizedRanges(each)); return char('^').optional().seq(positive).map((each) => each[0] == null ? each[1] : new _NotCharacterPredicate(each[1])); } final _patternParser = _createPatternParser(); class _RangesCharPredicate implements CharacterPredicate { final int length; final List starts; final List stops; _RangesCharPredicate(this.length, this.starts, this.stops); @override bool test(int value) { var min = 0; var max = length; while (min < max) { var mid = min + ((max - min) >> 1); var comp = starts[mid] - value; if (comp == 0) { return true; } else if (comp < 0) { min = mid + 1; } else { max = mid; } } return 0 < min && value <= stops[min - 1]; } } Parser range(start, stop, [String message]) { return new CharacterParser(new _RangeCharPredicate(_toCharCode(start), _toCharCode(stop)), message != null ? message : '$start..$stop expected'); } class _RangeCharPredicate implements CharacterPredicate { final int start; final int stop; _RangeCharPredicate(this.start, this.stop); @override bool test(int value) => start <= value && value <= stop; } Parser uppercase([String message]) { return new CharacterParser(_uppercaseCharPredicate, message != null ? message : 'uppercase letter expected'); } class _UppercaseCharPredicate implements CharacterPredicate { const _UppercaseCharPredicate(); @override bool test(int value) => 65 <= value && value <= 90; } const _uppercaseCharPredicate = const _UppercaseCharPredicate(); Parser whitespace([String message]) { return new CharacterParser(_whitespaceCharPredicate, message != null ? message : 'whitespace expected'); } class _WhitespaceCharPredicate implements CharacterPredicate { const _WhitespaceCharPredicate(); @override bool test(int value) { if (value < 256) { return value == 0x09 || value == 0x0A || value == 0x0B || value == 0x0C || value == 0x0D || value == 0x20 || value == 0x85 || value == 0xA0; } else { return value == 0x1680 || value == 0x180E || value == 0x2000 || value == 0x2001 || value == 0x2002 || value == 0x2003 || value == 0x2004 || value == 0x2005 || value == 0x2006 || value == 0x2007 || value == 0x2008 || value == 0x2009 || value == 0x200A || value == 0x2028 || value == 0x2029 || value == 0x202F || value == 0x205F || value == 0x3000 || value == 0xFEFF; } } } const _whitespaceCharPredicate = const _WhitespaceCharPredicate(); Parser word([String message]) { return new CharacterParser(_wordCharPredicate, message != null ? message : 'letter or digit expected'); } class _WordCharPredicate implements CharacterPredicate { const _WordCharPredicate(); @override bool test(int value) => (65 <= value && value <= 90) || (97 <= value && value <= 122) || (48 <= value && value <= 57) || (value == 95); } const _wordCharPredicate = const _WordCharPredicate(); int _toCharCode(element) { if (element is num) { return element.round(); } var value = element.toString(); if (value.length != 1) { throw new ArgumentError('$value is not a character'); } return value.codeUnitAt(0); } class DelegateParser extends Parser { Parser _delegate; DelegateParser(this._delegate); @override Result parseOn(Context context) { return _delegate.parseOn(context); } @override List get children => [_delegate]; @override void replace(Parser source, Parser target) { super.replace(source, target); if (_delegate == source) { _delegate = target; } } @override Parser copy() => new DelegateParser(_delegate); } class EndOfInputParser extends DelegateParser { final String _message; EndOfInputParser(parser, this._message) : super(parser); @override Result parseOn(Context context) { var result = _delegate.parseOn(context); if (result.isFailure || result.position == result.buffer.length) { return result; } return result.failure(_message, result.position); } @override String toString() => '${super.toString()}[$_message]'; @override Parser copy() => new EndOfInputParser(_delegate, _message); @override bool hasEqualProperties(Parser other) { return other is EndOfInputParser && super.hasEqualProperties(other) && _message == other._message; } } class AndParser extends DelegateParser { AndParser(parser) : super(parser); @override Result parseOn(Context context) { var result = _delegate.parseOn(context); if (result.isSuccess) { return context.success(result.value); } else { return result; } } @override Parser copy() => new AndParser(_delegate); } class NotParser extends DelegateParser { final String _message; NotParser(parser, this._message) : super(parser); @override Result parseOn(Context context) { var result = _delegate.parseOn(context); if (result.isFailure) { return context.success(null); } else { return context.failure(_message); } } @override String toString() => '${super.toString()}[$_message]'; @override Parser copy() => new NotParser(_delegate, _message); @override bool hasEqualProperties(Parser other) { return other is NotParser && super.hasEqualProperties(other) && _message == other._message; } } class OptionalParser extends DelegateParser { final _otherwise; OptionalParser(parser, this._otherwise) : super(parser); @override Result parseOn(Context context) { var result = _delegate.parseOn(context); if (result.isSuccess) { return result; } else { return context.success(_otherwise); } } @override Parser copy() => new OptionalParser(_delegate, _otherwise); @override bool hasEqualProperties(Parser other) { return other is OptionalParser && super.hasEqualProperties(other) && _otherwise == other._otherwise; } } abstract class ListParser extends Parser { final List _parsers; ListParser(this._parsers); @override List get children => _parsers; @override void replace(Parser source, Parser target) { super.replace(source, target); for (var i = 0; i < _parsers.length; i++) { if (_parsers[i] == source) { _parsers[i] = target; } } } } class ChoiceParser extends ListParser { factory ChoiceParser(Iterable parsers) { return new ChoiceParser._(new List.from(parsers, growable: false)); } ChoiceParser._(parsers) : super(parsers); @override Result parseOn(Context context) { var result; for (var i = 0; i < _parsers.length; i++) { result = _parsers[i].parseOn(context); if (result.isSuccess) { return result; } } return result; } @override Parser or(Parser other) { return new ChoiceParser(new List() ..addAll(_parsers) ..add(other)); } @override Parser copy() => new ChoiceParser(_parsers); } class SequenceParser extends ListParser { factory SequenceParser(Iterable parsers) { return new SequenceParser._(new List.from(parsers, growable: false)); } SequenceParser._(parsers) : super(parsers); @override Result parseOn(Context context) { var current = context; var elements = new List(_parsers.length); for (var i = 0; i < _parsers.length; i++) { var result = _parsers[i].parseOn(current); if (result.isFailure) { return result; } elements[i] = result.value; current = result; } return current.success(elements); } @override Parser seq(Parser other) { return new SequenceParser(new List() ..addAll(_parsers) ..add(other)); } @override Parser copy() => new SequenceParser(_parsers); } @deprecated abstract class CompositeParser extends DelegateParser { bool _completed = false; final Map _defined = new Map(); final Map _undefined = new Map(); CompositeParser() : super(failure('Uninitalized production: start')) { initialize(); _complete(); } void initialize(); void _complete() { _delegate = ref('start'); _undefined.forEach((name, parser) { if (!_defined.containsKey(name)) { throw new UndefinedProductionError(name); } parser.set(_defined[name]); }); _undefined.clear(); _completed = true; _delegate = ref('start'); } Parser ref(String name) { if (_completed) { if (_defined.containsKey(name)) { return _defined[name]; } else { throw new UndefinedProductionError(name); } } else { return _undefined.putIfAbsent(name, () { return failure('Uninitalized production: $name').settable(); }); } } Parser operator [](String name) => ref(name); void def(String name, Parser parser) { if (_completed) { throw new CompletedParserError(); } else if (_defined.containsKey(name)) { throw new RedefinedProductionError(name); } else { _defined[name] = parser; } } void redef(String name, replacement) { if (_completed) { throw new CompletedParserError(); } else if (!_defined.containsKey(name)) { throw new UndefinedProductionError(name); } else { _defined[name] = replacement is Parser ? replacement : replacement(_defined[name]); } } void action(String name, Function function) { redef(name, (parser) => parser.map(function)); } } class CompletedParserError extends Error { CompletedParserError(); @override String toString() => 'Completed parser'; } class UndefinedProductionError extends Error { final String name; UndefinedProductionError(this.name); @override String toString() => 'Undefined production: $name'; } class RedefinedProductionError extends Error { final String name; RedefinedProductionError(this.name); @override String toString() => 'Redefined production: $name'; } class Context { const Context(this.buffer, this.position); final buffer; final int position; Result success(result, [int position]) { return new Success(buffer, position == null ? this.position : position, result); } Result failure(String message, [int position]) { return new Failure(buffer, position == null ? this.position : position, message); } String toString() => 'Context[${toPositionString()}]'; String toPositionString() => Token.positionString(buffer, position); } abstract class Result extends Context { const Result(buffer, position) : super(buffer, position); bool get isSuccess => false; bool get isFailure => false; get value; String get message; } class Success extends Result { const Success(buffer, position, this.value) : super(buffer, position); @override bool get isSuccess => true; @override final value; @override String get message => null; @override String toString() => 'Success[${toPositionString()}]: $value'; } class Failure extends Result { const Failure(buffer, position, this.message) : super(buffer, position); @override bool get isFailure => true; @override get value => throw new ParserError(this); @override final String message; @override String toString() => 'Failure[${toPositionString()}]: $message'; } class ParserError extends Error { final Failure failure; ParserError(this.failure); @override String toString() => '${failure.message} at ${failure.toPositionString()}'; } abstract class GrammarDefinition { Parser start(); Parser ref(Function function, [arg1, arg2, arg3, arg4, arg5, arg6]) { var arguments = [arg1, arg2, arg3, arg4, arg5, arg6].takeWhile((each) => each != null).toList(growable: false); return new _Reference(function, arguments); } Parser build({Function start: null, List arguments: const []}) { return _resolve(new _Reference(start != null ? start : this.start, arguments)); } Parser _resolve(_Reference reference) { var mapping = new Map(); Parser _dereference(_Reference reference) { var parser = mapping[reference]; if (parser == null) { var references = [reference]; parser = reference.resolve(); while (parser is _Reference) { if (references.contains(parser)) { throw new StateError('Recursive references detected: $references'); } references.add(parser); parser = parser.resolve(); } for (var each in references) { mapping[each] = parser; } } return parser; } var todo = [_dereference(reference)]; var seen = new Set.from(todo); while (todo.isNotEmpty) { var parent = todo.removeLast(); for (var child in parent.children) { if (child is _Reference) { var referenced = _dereference(child); parent.replace(child, referenced); child = referenced; } if (!seen.contains(child)) { seen.add(child); todo.add(child); } } } return mapping[reference]; } } class GrammarParser extends DelegateParser { GrammarParser(GrammarDefinition definition) : super(definition.build()); } class _Reference extends Parser { final Function function; final List arguments; _Reference(this.function, this.arguments); Parser resolve() => Function.apply(function, arguments); @override bool operator ==(other) { if (other is! _Reference || other.function != function || other.arguments.length != arguments.length) { return false; } for (var i = 0; i < arguments.length; i++) { var a = arguments[i], b = other.arguments[i]; if (a is Parser && a is! _Reference && b is Parser && b is! _Reference) { if (!a.isEqualTo(b)) { return false; } } else { if (a != b) { return false; } } } return true; } @override int get hashCode => function.hashCode; @override Parser copy() => throw new UnsupportedError('References cannot be copied.'); @override Result parseOn(Context context) => throw new UnsupportedError('References cannot be parsed.'); } class ExpressionBuilder { final List _groups = new List(); ExpressionGroup group() { var group = new ExpressionGroup(); _groups.add(group); return group; } Parser build() => _groups.fold(failure('Highest priority group should define a primitive parser.'), (a, b) => b._build(a)); } class ExpressionGroup { void primitive(Parser parser) { _primitives.add(parser); } Parser _build_primitive(Parser inner) { return _build_choice(_primitives, inner); } final List _primitives = new List(); void prefix(Parser parser, [action(operator, value)]) { if (action == null) action = (operator, value) => [operator, value]; _prefix.add(parser.map((operator) => new _ExpressionResult(operator, action))); } Parser _build_prefix(Parser inner) { if (_prefix.isEmpty) { return inner; } else { return new SequenceParser([_build_choice(_prefix).star(), inner]).map((tuple) { return tuple.first.reversed.fold(tuple.last, (value, result) { return result.action(result.operator, value); }); }); } } final List _prefix = new List(); void postfix(Parser parser, [action(value, operator)]) { if (action == null) action = (value, operator) => [value, operator]; _postfix.add(parser.map((operator) => new _ExpressionResult(operator, action))); } Parser _build_postfix(Parser inner) { if (_postfix.isEmpty) { return inner; } else { return new SequenceParser([inner, _build_choice(_postfix).star()]).map((tuple) { return tuple.last.fold(tuple.first, (value, result) { return result.action(value, result.operator); }); }); } } final List _postfix = new List(); void right(Parser parser, [action(left, operator, right)]) { if (action == null) action = (left, operator, right) => [left, operator, right]; _right.add(parser.map((operator) => new _ExpressionResult(operator, action))); } Parser _build_right(Parser inner) { if (_right.isEmpty) { return inner; } else { return inner.separatedBy(_build_choice(_right)).map((sequence) { var result = sequence.last; for (var i = sequence.length - 2; i > 0; i -= 2) { result = sequence[i].action(sequence[i - 1], sequence[i].operator, result); } return result; }); } } final List _right = new List(); void left(Parser parser, [action(left, operator, right)]) { if (action == null) action = (left, operator, right) => [left, operator, right]; _left.add(parser.map((operator) => new _ExpressionResult(operator, action))); } Parser _build_left(Parser inner) { if (_left.isEmpty) { return inner; } else { return inner.separatedBy(_build_choice(_left)).map((sequence) { var result = sequence.first; for (var i = 1; i < sequence.length; i += 2) { result = sequence[i].action(result, sequence[i].operator, sequence[i + 1]); } return result; }); } } final List _left = new List(); Parser _build_choice(List parsers, [Parser otherwise]) { if (parsers.isEmpty) { return otherwise; } else if (parsers.length == 1) { return parsers.first; } else { return new ChoiceParser(parsers); } } Parser _build(Parser inner) { return _build_left(_build_right(_build_postfix(_build_prefix(_build_primitive(inner))))); } } class _ExpressionResult { final operator; final Function action; _ExpressionResult(this.operator, this.action); String toString() => operator.toString(); } abstract class Parser { Result parseOn(Context context); Result parse(input) { return parseOn(new Context(input, 0)); } bool accept(input) { return parse(input).isSuccess; } Iterable matches(input) { var list = new List(); and().map((each) => list.add(each)).seq(any()).or(any()).star().parse(input); return list; } Iterable matchesSkipping(input) { var list = new List(); map((each) => list.add(each)).or(any()).star().parse(input); return list; } Parser optional([otherwise]) => new OptionalParser(this, otherwise); Parser star() => repeat(0, unbounded); Parser starGreedy(Parser limit) => repeatGreedy(limit, 0, unbounded); Parser starLazy(Parser limit) => repeatLazy(limit, 0, unbounded); Parser plus() => repeat(1, unbounded); Parser plusGreedy(Parser limit) => repeatGreedy(limit, 1, unbounded); Parser plusLazy(Parser limit) => repeatLazy(limit, 1, unbounded); Parser repeat(int min, int max) { return new PossessiveRepeatingParser(this, min, max); } Parser repeatGreedy(Parser limit, int min, int max) { return new GreedyRepeatingParser(this, limit, min, max); } Parser repeatLazy(Parser limit, int min, int max) { return new LazyRepeatingParser(this, limit, min, max); } Parser times(int count) => repeat(count, count); Parser seq(Parser other) => new SequenceParser([this, other]); Parser operator &(Parser other) => this.seq(other); Parser or(Parser other) => new ChoiceParser([this, other]); Parser operator |(Parser other) => this.or(other); Parser and() => new AndParser(this); Parser not([String message]) => new NotParser(this, message); Parser neg([String message]) => not(message).seq(any()).pick(1); Parser flatten() => new FlattenParser(this); Parser token() => new TokenParser(this); Parser trim([Parser left, Parser right]) { if (left == null) left = whitespace(); if (right == null) right = left; return new TrimmingParser(this, left, right); } Parser end([String message = 'end of input expected']) { return new EndOfInputParser(this, message); } SettableParser settable() => new SettableParser(this); Parser map(Function function) => new ActionParser(this, function); Parser pick(int index) { return this.map((List list) { return list[index < 0 ? list.length + index : index]; }); } Parser permute(List indexes) { return this.map((List list) { return indexes.map((index) { return list[index < 0 ? list.length + index : index]; }).toList(); }); } Parser separatedBy(Parser separator, {bool includeSeparators: true, bool optionalSeparatorAtEnd: false}) { var repeater = new SequenceParser([separator, this]).star(); var parser = new SequenceParser(optionalSeparatorAtEnd ? [this, repeater, separator.optional(separator)] : [this, repeater]); return parser.map((List list) { var result = new List(); result.add(list[0]); for (var tuple in list[1]) { if (includeSeparators) { result.add(tuple[0]); } result.add(tuple[1]); } if (includeSeparators && optionalSeparatorAtEnd && !identical(list[2], separator)) { result.add(list[2]); } return result; }); } Parser copy(); bool isEqualTo(Parser other, [Set seen]) { if (seen == null) { seen = new Set(); } if (this == other || seen.contains(this)) { return true; } seen.add(this); return runtimeType == other.runtimeType && hasEqualProperties(other) && hasEqualChildren(other, seen); } bool hasEqualProperties(Parser other) => true; bool hasEqualChildren(Parser other, Set seen) { var thisChildren = children, otherChildren = other.children; if (thisChildren.length != otherChildren.length) { return false; } for (var i = 0; i < thisChildren.length; i++) { if (!thisChildren[i].isEqualTo(otherChildren[i], seen)) { return false; } } return true; } List get children => const []; void replace(Parser source, Parser target) {} } Parser epsilon([result]) => new EpsilonParser(result); class EpsilonParser extends Parser { final _result; EpsilonParser(this._result); @override Result parseOn(Context context) => context.success(_result); @override Parser copy() => new EpsilonParser(_result); @override bool hasEqualProperties(Parser other) { return other is EpsilonParser && super.hasEqualProperties(other) && _result == other._result; } } Parser failure([String message = 'unable to parse']) { return new FailureParser(message); } class FailureParser extends Parser { final String _message; FailureParser(this._message); @override Result parseOn(Context context) => context.failure(_message); @override String toString() => '${super.toString()}[$_message]'; @override Parser copy() => new FailureParser(_message); @override bool hasEqualProperties(Parser other) { return other is FailureParser && super.hasEqualProperties(other) && _message == other._message; } } SettableParser undefined([String message = 'undefined parser']) { return failure(message).settable(); } class SettableParser extends DelegateParser { SettableParser(parser) : super(parser); void set(Parser parser) => replace(children[0], parser); @override Parser copy() => new SettableParser(_delegate); } Parser any([String message = 'input expected']) { return new AnyParser(message); } class AnyParser extends Parser { final String _message; AnyParser(this._message); @override Result parseOn(Context context) { var position = context.position; var buffer = context.buffer; return position < buffer.length ? context.success(buffer[position], position + 1) : context.failure(_message); } @override Parser copy() => new AnyParser(_message); @override bool hasEqualProperties(Parser other) { return other is AnyParser && super.hasEqualProperties(other) && _message == other._message; } } Parser anyIn(elements, [String message]) { return predicate(1, (each) => elements.indexOf(each) >= 0, message != null ? message : 'any of $elements expected'); } Parser string(String element, [String message]) { return predicate(element.length, (String each) => element == each, message != null ? message : '$element expected'); } Parser stringIgnoreCase(String element, [String message]) { final lowerElement = element.toLowerCase(); return predicate(element.length, (String each) => lowerElement == each.toLowerCase(), message != null ? message : '$element expected'); } typedef bool Predicate(input); Parser predicate(int length, Predicate predicate, String message) { return new PredicateParser(length, predicate, message); } class PredicateParser extends Parser { final int _length; final Predicate _predicate; final String _message; PredicateParser(this._length, this._predicate, this._message); @override Result parseOn(Context context) { final start = context.position; final stop = start + _length; if (stop <= context.buffer.length) { var result = context.buffer is String ? context.buffer.substring(start, stop) : context.buffer.sublist(start, stop); if (_predicate(result)) { return context.success(result, stop); } } return context.failure(_message); } @override String toString() => '${super.toString()}[$_message]'; @override Parser copy() => new PredicateParser(_length, _predicate, _message); @override bool hasEqualProperties(Parser other) { return other is PredicateParser && super.hasEqualProperties(other) && _length == other._length && _predicate == other._predicate && _message == other._message; } } const int unbounded = -1; abstract class RepeatingParser extends DelegateParser { final int _min; final int _max; RepeatingParser(Parser parser, this._min, this._max) : super(parser) { assert(0 <= _min); assert(_max == unbounded || _min <= _max); } @override String toString() { var max = _max == unbounded ? '*' : _max; return '${super.toString()}[$_min..$max]'; } @override bool hasEqualProperties(Parser other) { return other is RepeatingParser && super.hasEqualProperties(other) && _min == other._min && _max == other._max; } } class PossessiveRepeatingParser extends RepeatingParser { PossessiveRepeatingParser(Parser parser, int min, int max) : super(parser, min, max); @override Result parseOn(Context context) { var current = context; var elements = new List(); while (elements.length < _min) { var result = _delegate.parseOn(current); if (result.isFailure) { return result; } elements.add(result.value); current = result; } while (_max == unbounded || elements.length < _max) { var result = _delegate.parseOn(current); if (result.isFailure) { return current.success(elements); } elements.add(result.value); current = result; } return current.success(elements); } @override Parser copy() => new PossessiveRepeatingParser(_delegate, _min, _max); } abstract class LimitedRepeatingParser extends RepeatingParser { Parser _limit; LimitedRepeatingParser(Parser parser, this._limit, int min, int max) : super(parser, min, max); @override List get children => [_delegate, _limit]; @override void replace(Parser source, Parser target) { super.replace(source, target); if (_limit == source) { _limit = target; } } } class GreedyRepeatingParser extends LimitedRepeatingParser { GreedyRepeatingParser(Parser parser, Parser limit, int min, int max) : super(parser, limit, min, max); @override Result parseOn(Context context) { var current = context; var elements = new List(); while (elements.length < _min) { var result = _delegate.parseOn(current); if (result.isFailure) { return result; } elements.add(result.value); current = result; } var contexts = new List.from([current]); while (_max == unbounded || elements.length < _max) { var result = _delegate.parseOn(current); if (result.isFailure) { break; } elements.add(result.value); contexts.add(current = result); } while (true) { var limit = _limit.parseOn(contexts.last); if (limit.isSuccess) { return contexts.last.success(elements); } if (elements.isEmpty) { return limit; } contexts.removeLast(); elements.removeLast(); if (contexts.isEmpty) { return limit; } } } @override Parser copy() => new GreedyRepeatingParser(_delegate, _limit, _min, _max); } class LazyRepeatingParser extends LimitedRepeatingParser { LazyRepeatingParser(Parser parser, Parser limit, int min, int max) : super(parser, limit, min, max); @override Result parseOn(Context context) { var current = context; var elements = new List(); while (elements.length < _min) { var result = _delegate.parseOn(current); if (result.isFailure) { return result; } elements.add(result.value); current = result; } while (true) { var limit = _limit.parseOn(current); if (limit.isSuccess) { return current.success(elements); } else { if (_max != unbounded && elements.length >= _max) { return limit; } var result = _delegate.parseOn(current); if (result.isFailure) { return limit; } elements.add(result.value); current = result; } } } @override Parser copy() => new LazyRepeatingParser(_delegate, _limit, _min, _max); } class Token { final value; final buffer; final int start; final int stop; const Token(this.value, this.buffer, this.start, this.stop); get input => buffer is String ? buffer.substring(start, stop) : buffer.sublist(start, stop); int get length => stop - start; int get line => Token.lineAndColumnOf(buffer, start)[0]; int get column => Token.lineAndColumnOf(buffer, start)[1]; @override String toString() => 'Token[${positionString(buffer, start)}]: $value'; @override bool operator ==(other) { return other is Token && value == other.value && start == other.start && stop == other.stop; } @override int get hashCode => value.hashCode + start.hashCode + stop.hashCode; static final Parser _NEWLINE_PARSER = char('\n').or(char('\r').seq(char('\n').optional())); static Parser newlineParser() => _NEWLINE_PARSER; static List lineAndColumnOf(String buffer, int position) { var line = 1, offset = 0; for (var token in newlineParser().token().matchesSkipping(buffer)) { if (position < token.stop) { return [line, position - offset + 1]; } line++; offset = token.stop; } return [line, position - offset + 1]; } static String positionString(buffer, int position) { if (buffer is String) { var lineAndColumn = Token.lineAndColumnOf(buffer, position); return '${lineAndColumn[0]}:${lineAndColumn[1]}'; } else { return '${position}'; } } }