Updated combinator
This commit is contained in:
parent
7adeff9a63
commit
c2c9e40836
21 changed files with 82 additions and 79 deletions
|
@ -1,7 +1,7 @@
|
|||
part of lex.src.combinator;
|
||||
|
||||
class _Cache<T> extends Parser<T> {
|
||||
final Map<int, ParseResult<T>?> _cache = {};
|
||||
final Map<int, ParseResult<T>> _cache = {};
|
||||
final Parser<T> parser;
|
||||
|
||||
_Cache(this.parser);
|
||||
|
@ -10,7 +10,7 @@ class _Cache<T> extends Parser<T> {
|
|||
ParseResult<T> __parse(ParseArgs args) {
|
||||
return _cache.putIfAbsent(args.scanner.position, () {
|
||||
return parser._parse(args.increaseDepth());
|
||||
})!.change(parser: this);
|
||||
}).change(parser: this);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -8,7 +8,7 @@ class _Cast<T, U extends T> extends Parser<U> {
|
|||
@override
|
||||
ParseResult<U> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
return new ParseResult<U>(
|
||||
return ParseResult<U>(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
this,
|
||||
|
@ -39,7 +39,7 @@ class _CastDynamic<T> extends Parser<dynamic> {
|
|||
@override
|
||||
ParseResult<dynamic> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
return new ParseResult<dynamic>(
|
||||
return ParseResult<dynamic>(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
this,
|
||||
|
|
|
@ -5,7 +5,7 @@ part of lex.src.combinator;
|
|||
/// If [failFast] is `true` (default), then the first failure to parse will abort the parse.
|
||||
ListParser<T> chain<T>(Iterable<Parser<T>> parsers,
|
||||
{bool failFast: true, SyntaxErrorSeverity? severity}) {
|
||||
return new _Chain<T>(
|
||||
return _Chain<T>(
|
||||
parsers, failFast != false, severity ?? SyntaxErrorSeverity.error);
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ class _Alt<T> extends Parser<T> {
|
|||
return result.successful
|
||||
? result
|
||||
: result.addErrors([
|
||||
new SyntaxError(
|
||||
SyntaxError(
|
||||
severity, errorMessage, result.span ?? args.scanner.emptySpan),
|
||||
]);
|
||||
}
|
||||
|
@ -43,8 +43,8 @@ class _Chain<T> extends ListParser<T> {
|
|||
@override
|
||||
ParseResult<List<T>> __parse(ParseArgs args) {
|
||||
var errors = <SyntaxError>[];
|
||||
var results = <T?>[];
|
||||
var spans = <FileSpan?>[];
|
||||
var results = <T>[];
|
||||
var spans = <FileSpan>[];
|
||||
bool successful = true;
|
||||
|
||||
for (var parser in parsers) {
|
||||
|
@ -54,32 +54,36 @@ class _Chain<T> extends ListParser<T> {
|
|||
if (parser is _Alt) errors.addAll(result.errors);
|
||||
|
||||
if (failFast) {
|
||||
return new ParseResult(
|
||||
return ParseResult(
|
||||
args.trampoline, args.scanner, this, false, result.errors);
|
||||
}
|
||||
|
||||
successful = false;
|
||||
}
|
||||
|
||||
results.add(result.value);
|
||||
if (result.value != null) {
|
||||
results.add(result.value!);
|
||||
}
|
||||
|
||||
if (result.span != null) spans.add(result.span);
|
||||
if (result.span != null) {
|
||||
spans.add(result.span!);
|
||||
}
|
||||
}
|
||||
|
||||
FileSpan? span;
|
||||
|
||||
if (spans.isNotEmpty) {
|
||||
span = spans.reduce((a, b) => a!.expand(b!));
|
||||
span = spans.reduce((a, b) => a.expand(b));
|
||||
}
|
||||
|
||||
return new ParseResult<List<T>>(
|
||||
return ParseResult<List<T>>(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
this,
|
||||
successful,
|
||||
errors,
|
||||
span: span,
|
||||
value: new List<T>.unmodifiable(results),
|
||||
value: List<T>.unmodifiable(results),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,11 +16,10 @@ class _Check<T> extends Parser<T> {
|
|||
return result;
|
||||
else if (!matcher.matches(result.value, matchState)) {
|
||||
return result.change(successful: false).addErrors([
|
||||
new SyntaxError(
|
||||
SyntaxError(
|
||||
severity,
|
||||
errorMessage ??
|
||||
matcher.describe(new StringDescription('Expected ')).toString() +
|
||||
'.',
|
||||
matcher.describe(StringDescription('Expected ')).toString() + '.',
|
||||
result.span,
|
||||
),
|
||||
]);
|
||||
|
@ -30,7 +29,7 @@ class _Check<T> extends Parser<T> {
|
|||
|
||||
@override
|
||||
void stringify(CodeBuffer buffer) {
|
||||
var d = matcher.describe(new StringDescription());
|
||||
var d = matcher.describe(StringDescription());
|
||||
buffer
|
||||
..writeln('check($d) (')
|
||||
..indent();
|
||||
|
|
|
@ -249,7 +249,7 @@ abstract class Parser<T> {
|
|||
ListParser<dynamic> then(Parser other) => chain<dynamic>([this, other]);
|
||||
|
||||
/// Casts this instance into a [ListParser].
|
||||
ListParser<T?> toList() => _ToList<T>(this);
|
||||
ListParser<T> toList() => _ToList<T>(this);
|
||||
|
||||
/// Consumes and ignores any trailing occurrences of [pattern].
|
||||
Parser<T> trail(Pattern pattern) =>
|
||||
|
@ -371,7 +371,7 @@ class ParseResult<T> {
|
|||
ParseResult<T> change(
|
||||
{Parser<T>? parser,
|
||||
bool? successful,
|
||||
Iterable<SyntaxError>? errors,
|
||||
Iterable<SyntaxError> errors = const [],
|
||||
FileSpan? span,
|
||||
T? value}) {
|
||||
return ParseResult<T>(
|
||||
|
@ -379,7 +379,7 @@ class ParseResult<T> {
|
|||
scanner,
|
||||
parser ?? this.parser,
|
||||
successful ?? this.successful,
|
||||
errors ?? this.errors,
|
||||
errors,
|
||||
span: span ?? this.span,
|
||||
value: value ?? this.value,
|
||||
);
|
||||
|
|
|
@ -13,8 +13,8 @@ class _Compare<T> extends ListParser<T> {
|
|||
|
||||
result = result.change(
|
||||
value: result.value?.isNotEmpty == true ? result.value : []);
|
||||
result = result.change(value: new List<T>.from(result.value!));
|
||||
return new ParseResult<List<T>>(
|
||||
result = result.change(value: List<T>.from(result.value!));
|
||||
return ParseResult<List<T>>(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
this,
|
||||
|
|
|
@ -14,7 +14,7 @@ class _Index<T> extends Parser<T> {
|
|||
if (result.successful)
|
||||
value = index == -1 ? result.value!.last : result.value!.elementAt(index);
|
||||
|
||||
return new ParseResult<T>(
|
||||
return ParseResult<T>(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
this,
|
||||
|
|
|
@ -5,8 +5,7 @@ part of lex.src.combinator;
|
|||
/// You can provide a custom [errorMessage].
|
||||
Parser<T> longest<T>(Iterable<Parser<T>> parsers,
|
||||
{Object? errorMessage, SyntaxErrorSeverity? severity}) {
|
||||
return new _Longest(
|
||||
parsers, errorMessage, severity ?? SyntaxErrorSeverity.error);
|
||||
return _Longest(parsers, errorMessage, severity ?? SyntaxErrorSeverity.error);
|
||||
}
|
||||
|
||||
class _Longest<T> extends Parser<T> {
|
||||
|
@ -23,7 +22,7 @@ class _Longest<T> extends Parser<T> {
|
|||
.where((p) => !args.trampoline.isActive(p, args.scanner.position));
|
||||
|
||||
if (inactive.isEmpty) {
|
||||
return new ParseResult(args.trampoline, args.scanner, this, false, []);
|
||||
return ParseResult(args.trampoline, args.scanner, this, false, []);
|
||||
}
|
||||
|
||||
int replay = args.scanner.position;
|
||||
|
@ -48,7 +47,7 @@ class _Longest<T> extends Parser<T> {
|
|||
|
||||
if (errorMessage != false)
|
||||
errors.add(
|
||||
new SyntaxError(
|
||||
SyntaxError(
|
||||
severity,
|
||||
errorMessage?.toString() ??
|
||||
'No match found for ${parsers.length} alternative(s)',
|
||||
|
@ -56,7 +55,7 @@ class _Longest<T> extends Parser<T> {
|
|||
),
|
||||
);
|
||||
|
||||
return new ParseResult(args.trampoline, args.scanner, this, false, errors);
|
||||
return ParseResult(args.trampoline, args.scanner, this, false, errors);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -82,7 +81,7 @@ class _Longest<T> extends Parser<T> {
|
|||
}
|
||||
|
||||
errors.add(
|
||||
new SyntaxError(
|
||||
SyntaxError(
|
||||
severity,
|
||||
errorMessage?.toString() ??
|
||||
'No match found for ${parsers.length} alternative(s)',
|
||||
|
@ -90,7 +89,7 @@ class _Longest<T> extends Parser<T> {
|
|||
),
|
||||
);
|
||||
|
||||
return new ParseResult(args.trampoline, args.scanner, this, false, errors);
|
||||
return ParseResult(args.trampoline, args.scanner, this, false, errors);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -3,7 +3,7 @@ part of lex.src.combinator;
|
|||
/// Expects to match a given [pattern]. If it is not matched, you can provide a custom [errorMessage].
|
||||
Parser<T> match<T>(Pattern pattern,
|
||||
{String? errorMessage, SyntaxErrorSeverity? severity}) =>
|
||||
new _Match<T>(pattern, errorMessage, severity ?? SyntaxErrorSeverity.error);
|
||||
_Match<T>(pattern, errorMessage, severity ?? SyntaxErrorSeverity.error);
|
||||
|
||||
class _Match<T> extends Parser<T> {
|
||||
final Pattern pattern;
|
||||
|
@ -16,14 +16,14 @@ class _Match<T> extends Parser<T> {
|
|||
ParseResult<T> __parse(ParseArgs args) {
|
||||
var scanner = args.scanner;
|
||||
if (!scanner.scan(pattern))
|
||||
return new ParseResult(args.trampoline, scanner, this, false, [
|
||||
new SyntaxError(
|
||||
return ParseResult(args.trampoline, scanner, this, false, [
|
||||
SyntaxError(
|
||||
severity,
|
||||
errorMessage ?? 'Expected "$pattern".',
|
||||
scanner.emptySpan,
|
||||
),
|
||||
]);
|
||||
return new ParseResult<T>(
|
||||
return ParseResult<T>(
|
||||
args.trampoline,
|
||||
scanner,
|
||||
this,
|
||||
|
|
|
@ -9,7 +9,7 @@ class _MaxDepth<T> extends Parser<T> {
|
|||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
if (args.depth > cap) {
|
||||
return new ParseResult<T>(args.trampoline, args.scanner, this, false, []);
|
||||
return ParseResult<T>(args.trampoline, args.scanner, this, false, []);
|
||||
}
|
||||
|
||||
return parser._parse(args.increaseDepth());
|
||||
|
|
|
@ -12,7 +12,7 @@ class _Negate<T> extends Parser<T> {
|
|||
var result = parser._parse(args.increaseDepth()).change(parser: this);
|
||||
|
||||
if (!result.successful) {
|
||||
return new ParseResult<T>(
|
||||
return ParseResult<T>(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
this,
|
||||
|
@ -27,7 +27,7 @@ class _Negate<T> extends Parser<T> {
|
|||
|
||||
if (errorMessage != null) {
|
||||
result = result.addErrors([
|
||||
new SyntaxError(
|
||||
SyntaxError(
|
||||
severity,
|
||||
errorMessage,
|
||||
result.span,
|
||||
|
|
|
@ -13,7 +13,7 @@ class Recursion<T> {
|
|||
postfix ??= {};
|
||||
}
|
||||
|
||||
Parser<T> precedence(int p) => new _Precedence(this, p);
|
||||
Parser<T> precedence(int p) => _Precedence(this, p);
|
||||
|
||||
void stringify(CodeBuffer buffer) {
|
||||
buffer
|
||||
|
@ -106,7 +106,7 @@ class _Precedence<T> extends Parser<T> {
|
|||
// If we're not done scanning, then we need some sort of guard to ensure the
|
||||
// that this exact parser does not run again in the exact position.
|
||||
}
|
||||
return new ParseResult(
|
||||
return ParseResult(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
this,
|
||||
|
@ -118,7 +118,7 @@ class _Precedence<T> extends Parser<T> {
|
|||
}
|
||||
}
|
||||
|
||||
return new ParseResult(
|
||||
return ParseResult(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
this,
|
||||
|
|
|
@ -11,7 +11,7 @@ class _Reduce<T> extends Parser<T> {
|
|||
ParseResult<List<T>> result = parser._parse(args.increaseDepth());
|
||||
|
||||
if (!result.successful)
|
||||
return new ParseResult<T>(
|
||||
return ParseResult<T>(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
this,
|
||||
|
@ -21,7 +21,7 @@ class _Reduce<T> extends Parser<T> {
|
|||
|
||||
result = result.change(
|
||||
value: result.value?.isNotEmpty == true ? result.value : []);
|
||||
return new ParseResult<T>(
|
||||
return ParseResult<T>(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
this,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
part of lex.src.combinator;
|
||||
|
||||
Reference<T> reference<T>() => new Reference<T>._();
|
||||
Reference<T> reference<T>() => Reference<T>._();
|
||||
|
||||
class Reference<T> extends Parser<T> {
|
||||
Parser<T>? _parser;
|
||||
|
@ -10,22 +10,21 @@ class Reference<T> extends Parser<T> {
|
|||
|
||||
void set parser(Parser<T> value) {
|
||||
if (_parser != null)
|
||||
throw new StateError(
|
||||
'There is already a parser assigned to this reference.');
|
||||
throw StateError('There is already a parser assigned to this reference.');
|
||||
_parser = value;
|
||||
}
|
||||
|
||||
@override
|
||||
ParseResult<T> __parse(ParseArgs args) {
|
||||
if (_parser == null)
|
||||
throw new StateError('There is no parser assigned to this reference.');
|
||||
throw StateError('There is no parser assigned to this reference.');
|
||||
return _parser!._parse(args);
|
||||
}
|
||||
|
||||
@override
|
||||
ParseResult<T> _parse(ParseArgs args) {
|
||||
if (_parser == null)
|
||||
throw new StateError('There is no parser assigned to this reference.');
|
||||
throw StateError('There is no parser assigned to this reference.');
|
||||
return _parser!._parse(args);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,15 +22,14 @@ class _Safe<T> extends Parser<T> {
|
|||
var errors = <SyntaxError>[];
|
||||
|
||||
errors.add(
|
||||
new SyntaxError(
|
||||
SyntaxError(
|
||||
severity,
|
||||
errorMessage,
|
||||
args.scanner.lastSpan ?? args.scanner.emptySpan,
|
||||
),
|
||||
);
|
||||
|
||||
return new ParseResult<T>(
|
||||
args.trampoline, args.scanner, this, false, errors);
|
||||
return ParseResult<T>(args.trampoline, args.scanner, this, false, errors);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,26 +1,30 @@
|
|||
part of lex.src.combinator;
|
||||
|
||||
class _ToList<T> extends ListParser<T?> {
|
||||
class _ToList<T> extends ListParser<T> {
|
||||
final Parser<T> parser;
|
||||
|
||||
_ToList(this.parser);
|
||||
|
||||
@override
|
||||
ParseResult<List<T?>> __parse(ParseArgs args) {
|
||||
ParseResult<List<T>> __parse(ParseArgs args) {
|
||||
var result = parser._parse(args.increaseDepth());
|
||||
|
||||
if (result.value is List) {
|
||||
return (result as ParseResult<List<T?>>).change(parser: this);
|
||||
return (result as ParseResult<List<T>>).change(parser: this);
|
||||
}
|
||||
|
||||
return new ParseResult(
|
||||
List<T> values = [];
|
||||
if (result.value != null) {
|
||||
values.add(result.value!);
|
||||
}
|
||||
return ParseResult(
|
||||
args.trampoline,
|
||||
args.scanner,
|
||||
this,
|
||||
result.successful,
|
||||
result.errors,
|
||||
span: result.span,
|
||||
value: [result.value],
|
||||
value: values,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,48 +1,48 @@
|
|||
part of lex.src.combinator;
|
||||
|
||||
/// A typed parser that parses a sequence of 2 values of different types.
|
||||
Parser<Tuple2<A?, B?>> tuple2<A, B>(Parser<A> a, Parser<B> b) {
|
||||
Parser<Tuple2<A, B>> tuple2<A, B>(Parser<A> a, Parser<B> b) {
|
||||
return chain([a, b]).map((r) {
|
||||
return Tuple2(r.value![0] as A?, r.value![1] as B?);
|
||||
return Tuple2(r.value?[0] as A, r.value?[1] as B);
|
||||
});
|
||||
}
|
||||
|
||||
/// A typed parser that parses a sequence of 3 values of different types.
|
||||
Parser<Tuple3<A?, B?, C?>> tuple3<A, B, C>(Parser<A> a, Parser<B> b, Parser<C> c) {
|
||||
Parser<Tuple3<A, B, C>> tuple3<A, B, C>(Parser<A> a, Parser<B> b, Parser<C> c) {
|
||||
return chain([a, b, c]).map((r) {
|
||||
return Tuple3(r.value![0] as A?, r.value![1] as B?, r.value![2] as C?);
|
||||
return Tuple3(r.value?[0] as A, r.value?[1] as B, r.value?[2] as C);
|
||||
});
|
||||
}
|
||||
|
||||
/// A typed parser that parses a sequence of 4 values of different types.
|
||||
Parser<Tuple4<A?, B?, C?, D?>> tuple4<A, B, C, D>(
|
||||
Parser<Tuple4<A, B, C, D>> tuple4<A, B, C, D>(
|
||||
Parser<A> a, Parser<B> b, Parser<C> c, Parser<D> d) {
|
||||
return chain([a, b, c, d]).map((r) {
|
||||
return Tuple4(
|
||||
r.value![0] as A?, r.value![1] as B?, r.value![2] as C?, r.value![3] as D?);
|
||||
r.value?[0] as A, r.value?[1] as B, r.value?[2] as C, r.value?[3] as D);
|
||||
});
|
||||
}
|
||||
|
||||
/// A typed parser that parses a sequence of 5 values of different types.
|
||||
Parser<Tuple5<A?, B?, C?, D?, E?>> tuple5<A, B, C, D, E>(
|
||||
Parser<Tuple5<A, B, C, D, E>> tuple5<A, B, C, D, E>(
|
||||
Parser<A> a, Parser<B> b, Parser<C> c, Parser<D> d, Parser<E> e) {
|
||||
return chain([a, b, c, d, e]).map((r) {
|
||||
return Tuple5(r.value![0] as A?, r.value![1] as B?, r.value![2] as C?,
|
||||
r.value![3] as D?, r.value![4] as E?);
|
||||
return Tuple5(r.value?[0] as A, r.value?[1] as B, r.value?[2] as C,
|
||||
r.value?[3] as D, r.value?[4] as E);
|
||||
});
|
||||
}
|
||||
|
||||
/// A typed parser that parses a sequence of 6 values of different types.
|
||||
Parser<Tuple6<A?, B?, C?, D?, E?, F?>> tuple6<A, B, C, D, E, F>(Parser<A> a,
|
||||
Parser<Tuple6<A, B, C, D, E, F>> tuple6<A, B, C, D, E, F>(Parser<A> a,
|
||||
Parser<B> b, Parser<C> c, Parser<D> d, Parser<E> e, Parser<F> f) {
|
||||
return chain([a, b, c, d, e, f]).map((r) {
|
||||
return Tuple6(r.value![0] as A?, r.value![1] as B?, r.value![2] as C?,
|
||||
r.value![3] as D?, r.value![4] as E?, r.value![5] as F?);
|
||||
return Tuple6(r.value?[0] as A, r.value?[1] as B, r.value?[2] as C,
|
||||
r.value?[3] as D, r.value?[4] as E, r.value?[5] as F);
|
||||
});
|
||||
}
|
||||
|
||||
/// A typed parser that parses a sequence of 7 values of different types.
|
||||
Parser<Tuple7<A?, B?, C?, D?, E?, F?, G?>> tuple7<A, B, C, D, E, F, G>(
|
||||
Parser<Tuple7<A, B, C, D, E, F, G>> tuple7<A, B, C, D, E, F, G>(
|
||||
Parser<A> a,
|
||||
Parser<B> b,
|
||||
Parser<C> c,
|
||||
|
@ -51,7 +51,7 @@ Parser<Tuple7<A?, B?, C?, D?, E?, F?, G?>> tuple7<A, B, C, D, E, F, G>(
|
|||
Parser<F> f,
|
||||
Parser<G> g) {
|
||||
return chain([a, b, c, d, e, f, g]).map((r) {
|
||||
return Tuple7(r.value![0] as A?, r.value![1] as B?, r.value![2] as C?,
|
||||
r.value![3] as D?, r.value![4] as E?, r.value![5] as F?, r.value![6] as G?);
|
||||
return Tuple7(r.value?[0] as A, r.value?[1] as B, r.value?[2] as C,
|
||||
r.value?[3] as D, r.value?[4] as E, r.value?[5] as F, r.value?[6] as G);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import 'package:string_scanner/string_scanner.dart';
|
||||
|
||||
SpanScanner scan(String text) => new SpanScanner(text);
|
||||
SpanScanner scan(String text) => SpanScanner(text);
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'common.dart';
|
|||
|
||||
main() {
|
||||
var number = chain([
|
||||
match(new RegExp(r'[0-9]+')).value((r) => int.parse(r.span!.text)),
|
||||
match(RegExp(r'[0-9]+')).value((r) => int.parse(r.span!.text)),
|
||||
match(',').opt(),
|
||||
]).first().cast<int>();
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ main() {
|
|||
});
|
||||
|
||||
test('check', () {
|
||||
var parser = match<int>(new RegExp(r'[A-Za-z]+'))
|
||||
var parser = match<int>(RegExp(r'[A-Za-z]+'))
|
||||
.value((r) => r.span!.length)
|
||||
.check(greaterThan(3));
|
||||
expect(parser.parse(scan('helloworld')).successful, isTrue);
|
||||
|
@ -27,8 +27,7 @@ main() {
|
|||
});
|
||||
|
||||
test('map', () {
|
||||
var parser =
|
||||
match(new RegExp(r'[A-Za-z]+')).map<int>((r) => r.span!.length);
|
||||
var parser = match(RegExp(r'[A-Za-z]+')).map<int>((r) => r.span!.length);
|
||||
expect(parser.parse(scan('hello')).value, 5);
|
||||
});
|
||||
|
||||
|
|
|
@ -6,12 +6,12 @@ void main() {}
|
|||
|
||||
/*
|
||||
void main() {
|
||||
var number = match(new RegExp(r'-?[0-9]+(\.[0-9]+)?'))
|
||||
var number = match( RegExp(r'-?[0-9]+(\.[0-9]+)?'))
|
||||
.map<num>((r) => num.parse(r.span.text));
|
||||
|
||||
var term = reference<num>();
|
||||
|
||||
var r = new Recursion<num>();
|
||||
var r = Recursion<num>();
|
||||
|
||||
r.prefix = [number];
|
||||
|
||||
|
@ -32,7 +32,7 @@ void main() {
|
|||
term.parser = r.precedence(0);
|
||||
|
||||
num parse(String text) {
|
||||
var scanner = new SpanScanner(text);
|
||||
var scanner = SpanScanner(text);
|
||||
var result = term.parse(scanner);
|
||||
print(result.span.highlight());
|
||||
return result.value;
|
||||
|
|
Loading…
Reference in a new issue