Skip to content

Commit

Permalink
#80: Attempt at abstracting over String, to be able to use Characters.
Browse files Browse the repository at this point in the history
  • Loading branch information
renggli committed Feb 7, 2021
1 parent 9f0addc commit 8468d7e
Show file tree
Hide file tree
Showing 47 changed files with 320 additions and 167 deletions.
38 changes: 38 additions & 0 deletions petitparser/lib/buffer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/// This package contains the parse input buffers.
import 'package:characters/characters.dart';

import 'src/buffer/character_buffer.dart';
import 'src/buffer/string_buffer.dart';

abstract class Buffer {
/// Create a buffer from the most appropriate internal implementation.
factory Buffer(/*Buffer|String|Characters*/ dynamic input) {
if (input is Buffer) {
return input;
} else if (input is String) {
return Buffer.fromString(input);
} else if (input is Characters) {
return Buffer.fromCharacters(input);
} else {
throw ArgumentError.value(input, 'input', 'Invalid input buffer');
}
}

/// Create a buffer from an input [String] (UTF-16).
factory Buffer.fromString(String input) => StringBuffer(input);

/// Create a buffer from input [Characters] (Unicode Grapheme).
factory Buffer.fromCharacters(Characters input) => CharactersBuffer(input);

/// Return the length of the buffer.
int get length;

/// Return the character at [position].
String charAt(int position);

/// Return the character code at [position].
int codeUnitAt(int position);

/// Return the substring between [start] and [stop] (exclusive).
String substring(int start, int stop);
}
1 change: 1 addition & 0 deletions petitparser/lib/petitparser.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// This package exports the core library of PetitParser, a dynamic parser
/// combinator framework.
export 'buffer.dart';
export 'context.dart';
export 'core.dart';
export 'definition.dart';
Expand Down
35 changes: 35 additions & 0 deletions petitparser/lib/src/buffer/character_buffer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'package:characters/characters.dart';

import '../../buffer.dart';

class CharactersBuffer implements Buffer {
final List<String> characters;

CharactersBuffer(Characters characters)
: characters = characters.toList(growable: false);

@override
int get length => characters.length;

@override
String charAt(int position) => characters[position];

@override
int codeUnitAt(int position) {
final chars = characters[position];
final charsLength = chars.length;
if (charsLength == 1) {
return chars.codeUnitAt(0);
} else {
var value = 0;
for (var i = 0; i < charsLength; i++) {
value = (value << 16) | chars.codeUnitAt(i);
}
return value;
}
}

@override
String substring(int start, int end) =>
characters.getRange(start, end).join();
}
22 changes: 22 additions & 0 deletions petitparser/lib/src/buffer/string_buffer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'package:meta/meta.dart';

import '../../buffer.dart';

@immutable
class StringBuffer implements Buffer {
final String string;

const StringBuffer(this.string);

@override
int get length => string.length;

@override
String charAt(int position) => string[position];

@override
int codeUnitAt(int position) => string.codeUnitAt(position);

@override
String substring(int start, int end) => string.substring(start, end);
}
3 changes: 2 additions & 1 deletion petitparser/lib/src/context/context.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:meta/meta.dart';

import '../../buffer.dart';
import '../core/token.dart';
import 'failure.dart';
import 'result.dart';
Expand All @@ -11,7 +12,7 @@ class Context {
const Context(this.buffer, this.position);

/// The buffer we are working on.
final String buffer;
final Buffer buffer;

/// The current position in the [buffer].
final int position;
Expand Down
3 changes: 2 additions & 1 deletion petitparser/lib/src/context/failure.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import '../../buffer.dart';
import '../core/exception.dart';
import 'result.dart';

/// An immutable parse result in case of a failed parse.
class Failure<R> extends Result<R> {
const Failure(String buffer, int position, this.message)
const Failure(Buffer buffer, int position, this.message)
: super(buffer, position);

@override
Expand Down
3 changes: 2 additions & 1 deletion petitparser/lib/src/context/result.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import '../../buffer.dart';
import '../core/exception.dart';
import 'context.dart';

/// An immutable parse result.
abstract class Result<R> extends Context {
const Result(String buffer, int position) : super(buffer, position);
const Result(Buffer buffer, int position) : super(buffer, position);

/// Returns `true` if this result indicates a parse success.
bool get isSuccess => false;
Expand Down
3 changes: 2 additions & 1 deletion petitparser/lib/src/context/success.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import '../../buffer.dart';
import 'result.dart';

/// An immutable parse result in case of a successful parse.
class Success<R> extends Result<R> {
const Success(String buffer, int position, this.value)
const Success(Buffer buffer, int position, this.value)
: super(buffer, position);

@override
Expand Down
3 changes: 2 additions & 1 deletion petitparser/lib/src/core/exception.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import '../../buffer.dart';
import '../context/failure.dart';

/// An exception raised in case of a parse error.
Expand All @@ -13,7 +14,7 @@ class ParserException implements FormatException {
int get offset => failure.position;

@override
String get source => failure.buffer;
Buffer get source => failure.buffer;

@override
String toString() => '${failure.message} at ${failure.toPositionString()}';
Expand Down
7 changes: 5 additions & 2 deletions petitparser/lib/src/core/parser.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:meta/meta.dart';

import '../../buffer.dart';
import '../context/context.dart';
import '../context/failure.dart';
import '../context/result.dart';
Expand Down Expand Up @@ -30,7 +31,7 @@ abstract class Parser<T> {
///
/// Subclasses don't necessarily have to override this method, since it is
/// emulated using its slower brother.
int fastParseOn(String buffer, int position) {
int fastParseOn(Buffer buffer, int position) {
final result = parseOn(Context(buffer, position));
return result.isSuccess ? result.position : -1;
}
Expand All @@ -47,7 +48,9 @@ abstract class Parser<T> {
/// Similarly, `letter().plus().parse('123')` results in an instance of
/// [Failure], where [Context.position] is `0` and [Failure.message] is
/// ['letter expected'].
Result<T> parse(String input) => parseOn(Context(input, 0));
@nonVirtual
Result<T> parse(/*Buffer|String|Characters*/ dynamic input) =>
parseOn(Context(Buffer(input), 0));

/// Returns a shallow copy of the receiver.
///
Expand Down
7 changes: 4 additions & 3 deletions petitparser/lib/src/core/token.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:meta/meta.dart';

import '../../buffer.dart';
import '../matcher/matches_skipping.dart';
import '../parser/action/token.dart';
import '../parser/character/char.dart';
Expand All @@ -23,7 +24,7 @@ class Token<T> {
final T value;

/// The parsed buffer of the token.
final String buffer;
final Buffer buffer;

/// The start position of the token in the buffer.
final int start;
Expand Down Expand Up @@ -64,7 +65,7 @@ class Token<T> {
char('\n') | (char('\r') & char('\n').optional());

/// Converts the [position] index in a [buffer] to a line and column tuple.
static List<int> lineAndColumnOf(String buffer, int position) {
static List<int> lineAndColumnOf(Buffer buffer, int position) {
var line = 1, offset = 0;
for (final token in newlineParser().token().matchesSkipping(buffer)) {
if (position < token.stop) {
Expand All @@ -78,7 +79,7 @@ class Token<T> {

/// Returns a human readable string representing the [position] index in a
/// [buffer].
static String positionString(String buffer, int position) {
static String positionString(Buffer buffer, int position) {
final lineAndColumn = lineAndColumnOf(buffer, position);
return '${lineAndColumn[0]}:${lineAndColumn[1]}';
}
Expand Down
3 changes: 2 additions & 1 deletion petitparser/lib/src/definition/parser.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import '../../buffer.dart';
import '../context/context.dart';
import '../context/result.dart';
import '../core/parser.dart';
Expand All @@ -14,7 +15,7 @@ class GrammarParser extends DelegateParser {
Result parseOn(Context context) => delegate.parseOn(context);

@override
int fastParseOn(String buffer, int position) =>
int fastParseOn(Buffer buffer, int position) =>
delegate.fastParseOn(buffer, position);

@override
Expand Down
3 changes: 2 additions & 1 deletion petitparser/lib/src/definition/reference.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:meta/meta.dart';

import '../../buffer.dart';
import '../context/context.dart';
import '../context/result.dart';
import '../core/parser.dart';
Expand Down Expand Up @@ -44,7 +45,7 @@ class Reference extends Parser {
throw UnsupportedError('References cannot be parsed.');

@override
int fastParseOn(String buffer, int position) =>
int fastParseOn(Buffer buffer, int position) =>
throw UnsupportedError('References cannot be parsed.');

@override
Expand Down
4 changes: 3 additions & 1 deletion petitparser/lib/src/matcher/accept.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import '../../buffer.dart';
import '../core/parser.dart';

extension AcceptParser<T> on Parser<T> {
/// Tests if the [input] can be successfully parsed.
///
/// For example, `letter().plus().accept('abc')` returns `true`, and
/// `letter().plus().accept('123')` returns `false`.
bool accept(String input) => fastParseOn(input, 0) >= 0;
bool accept(/*Buffer|String|Characters*/ dynamic input) =>
fastParseOn(Buffer(input), 0) >= 0;
}
5 changes: 3 additions & 2 deletions petitparser/lib/src/matcher/matches.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import '../../buffer.dart';
import '../core/parser.dart';
import '../parser/action/map.dart';
import '../parser/combinator/and.dart';
Expand All @@ -13,14 +14,14 @@ extension MatchesParser<T> on Parser<T> {
/// For example, `letter().plus().matches('abc de')` results in the list
/// `[['a', 'b', 'c'], ['b', 'c'], ['c'], ['d', 'e'], ['e']]`. See
/// [matchesSkipping] to retrieve non-overlapping parse results.
List<T> matches(String input) {
List<T> matches(/*Buffer|String|Characters*/ dynamic input) {
final list = <T>[];
and()
.map(list.add, hasSideEffects: true)
.seq(any())
.or(any())
.star()
.fastParseOn(input, 0);
.fastParseOn(Buffer(input), 0);
return list;
}
}
8 changes: 6 additions & 2 deletions petitparser/lib/src/matcher/matches_skipping.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import '../../buffer.dart';
import '../core/parser.dart';
import '../parser/action/map.dart';
import '../parser/combinator/choice.dart';
Expand All @@ -11,9 +12,12 @@ extension MatchesSkippingParser<T> on Parser<T> {
/// For example, `letter().plus().matchesSkipping('abc de')` results in the
/// list `[['a', 'b', 'c'], ['d', 'e']]`. See [matches] to retrieve
/// overlapping parse results.
List<T> matchesSkipping(String input) {
List<T> matchesSkipping(/*Buffer|String|Characters*/ dynamic input) {
final list = <T>[];
map(list.add, hasSideEffects: true).or(any()).star().fastParseOn(input, 0);
map(list.add, hasSideEffects: true)
.or(any())
.star()
.fastParseOn(Buffer(input), 0);
return list;
}
}
3 changes: 2 additions & 1 deletion petitparser/lib/src/matcher/pattern/parser_pattern.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:meta/meta.dart';

import '../../../buffer.dart';
import '../../core/parser.dart';
import 'parser_match.dart';
import 'pattern_iterable.dart';
Expand Down Expand Up @@ -31,7 +32,7 @@ class ParserPattern implements Pattern {
/// Returns `null` if the pattern doesn't match.
@override
ParserMatch? matchAsPrefix(String string, [int start = 0]) {
final end = parser.fastParseOn(string, start);
final end = parser.fastParseOn(Buffer.fromString(string), start);
return end < 0 ? null : ParserMatch(this, string, start, end);
}
}
9 changes: 6 additions & 3 deletions petitparser/lib/src/matcher/pattern/pattern_iterator.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import '../../../buffer.dart';
import '../../core/parser.dart';
import 'parser_match.dart';
import 'parser_pattern.dart';
Expand All @@ -6,17 +7,19 @@ class PatternIterator extends Iterator<ParserMatch> {
final ParserPattern pattern;
final Parser parser;
final String input;
final Buffer buffer;
int start;

PatternIterator(this.pattern, this.parser, this.input, this.start);
PatternIterator(this.pattern, this.parser, this.input, this.start)
: buffer = Buffer.fromString(input);

@override
late ParserMatch current;

@override
bool moveNext() {
while (start <= input.length) {
final end = parser.fastParseOn(input, start);
while (start <= buffer.length) {
final end = parser.fastParseOn(buffer, start);
if (end < 0) {
start++;
} else {
Expand Down
3 changes: 2 additions & 1 deletion petitparser/lib/src/parser/action/cast.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import '../../../buffer.dart';
import '../../context/context.dart';
import '../../context/result.dart';
import '../../core/parser.dart';
Expand All @@ -23,7 +24,7 @@ class CastParser<R> extends DelegateParser<R> {
}

@override
int fastParseOn(String buffer, int position) =>
int fastParseOn(Buffer buffer, int position) =>
delegate.fastParseOn(buffer, position);

@override
Expand Down
3 changes: 2 additions & 1 deletion petitparser/lib/src/parser/action/cast_list.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import '../../../buffer.dart';
import '../../context/context.dart';
import '../../context/result.dart';
import '../../core/parser.dart';
Expand All @@ -24,7 +25,7 @@ class CastListParser<R> extends DelegateParser<List<R>> {
}

@override
int fastParseOn(String buffer, int position) =>
int fastParseOn(Buffer buffer, int position) =>
delegate.fastParseOn(buffer, position);

@override
Expand Down
Loading

0 comments on commit 8468d7e

Please sign in to comment.