platform/packages/route/lib/src/grammar.dart

292 lines
7.5 KiB
Dart
Raw Normal View History

2017-11-27 02:21:19 +00:00
part of angel_route.src.router;
2017-11-28 21:07:14 +00:00
class RouteGrammar {
2019-02-03 05:33:47 +00:00
static const String notSlashRgx = r'([^/]+)';
2019-11-28 17:40:32 +00:00
//static final RegExp rgx = RegExp(r'\((.+)\)');
2017-11-27 02:21:19 +00:00
static final Parser<String> notSlash =
2019-11-28 17:40:32 +00:00
match<String>(RegExp(notSlashRgx)).value((r) => r.span.text);
2018-08-20 14:24:11 +00:00
2019-02-03 05:33:47 +00:00
static final Parser<Match> regExp =
2019-11-28 17:40:32 +00:00
match<Match>(RegExp(r'\(([^\n)]+)\)([^/]+)?'))
2019-02-03 05:33:47 +00:00
.value((r) => r.scanner.lastMatch);
2018-08-20 14:24:11 +00:00
2019-11-28 17:40:32 +00:00
static final Parser<Match> parameterName =
match<Match>(RegExp('$notSlashRgx?' r':([A-Za-z0-9_]+)' r'([^(/\n])?'))
.value((r) => r.scanner.lastMatch);
2017-11-27 02:21:19 +00:00
2017-11-28 21:07:14 +00:00
static final Parser<ParameterSegment> parameterSegment = chain([
2017-11-27 02:21:19 +00:00
parameterName,
2018-06-23 02:54:20 +00:00
match<bool>('?').value((r) => true).opt(),
2017-11-27 02:21:19 +00:00
regExp.opt(),
]).map((r) {
2019-02-03 05:33:47 +00:00
var match = r.value[0] as Match;
var rgxMatch = r.value[2] as Match;
2019-02-03 05:52:48 +00:00
var pre = match[1] ?? '';
var post = match[3] ?? '';
2019-02-03 05:33:47 +00:00
RegExp rgx;
if (rgxMatch != null) {
2019-02-03 05:52:48 +00:00
rgx = RegExp('(${rgxMatch[1]})');
post = (rgxMatch[2] ?? '') + post;
}
if (pre.isNotEmpty || post.isNotEmpty) {
if (rgx != null) {
var pattern = pre + rgx.pattern + post;
rgx = RegExp(pattern);
} else {
rgx = RegExp('$pre$notSlashRgx$post');
2019-02-03 05:33:47 +00:00
}
}
2019-11-28 17:40:32 +00:00
var s = ParameterSegment(match[2], rgx);
return r.value[1] == true ? OptionalSegment(s) : s;
2017-11-27 02:21:19 +00:00
});
2018-08-20 14:24:11 +00:00
static final Parser<ParsedParameterSegment> parsedParameterSegment = chain([
2019-11-28 17:40:32 +00:00
match(RegExp(r'(int|num|double)'),
2018-08-20 14:24:11 +00:00
errorMessage: 'Expected "int","double", or "num".')
.map((r) => r.span.text),
parameterSegment,
]).map((r) {
2019-11-28 17:40:32 +00:00
return ParsedParameterSegment(
2018-08-20 18:59:51 +00:00
r.value[0] as String, r.value[1] as ParameterSegment);
2018-08-20 14:24:11 +00:00
});
2017-11-28 21:07:14 +00:00
static final Parser<WildcardSegment> wildcardSegment =
2019-02-03 05:33:47 +00:00
match<WildcardSegment>(RegExp('$notSlashRgx?' r'\*' '$notSlashRgx?'))
.value((r) {
var m = r.scanner.lastMatch;
var pre = m[1] ?? '';
var post = m[2] ?? '';
2019-11-28 17:40:32 +00:00
return WildcardSegment(pre, post);
2019-02-03 05:33:47 +00:00
});
2017-11-27 02:21:19 +00:00
2017-11-28 21:07:14 +00:00
static final Parser<ConstantSegment> constantSegment =
2019-11-28 17:40:32 +00:00
notSlash.map<ConstantSegment>((r) => ConstantSegment(r.value));
2017-11-27 02:21:19 +00:00
2019-02-03 05:33:47 +00:00
static final Parser<SlashSegment> slashSegment =
match(SlashSegment.rgx).map((_) => SlashSegment());
static final Parser<RouteSegment> routeSegment = any([
//slashSegment,
2018-08-20 14:24:11 +00:00
parsedParameterSegment,
parameterSegment,
wildcardSegment,
constantSegment
]);
2017-11-27 02:21:19 +00:00
2019-02-03 05:33:47 +00:00
// static final Parser<RouteDefinition> routeDefinition = routeSegment
// .star()
2019-11-28 17:40:32 +00:00
// .map<RouteDefinition>((r) => RouteDefinition(r.value ?? []))
2019-02-03 05:33:47 +00:00
// .surroundedBy(match(RegExp(r'/*')).opt());
static final Parser slashes = match(RegExp(r'/*'));
2017-11-28 21:07:14 +00:00
static final Parser<RouteDefinition> routeDefinition = routeSegment
2019-02-03 05:33:47 +00:00
.separatedBy(slashes)
2019-11-28 17:40:32 +00:00
.map<RouteDefinition>((r) => RouteDefinition(r.value ?? []))
2019-02-03 05:33:47 +00:00
.surroundedBy(slashes.opt());
2017-11-27 02:21:19 +00:00
}
2017-11-28 21:07:14 +00:00
class RouteDefinition {
final List<RouteSegment> segments;
2017-11-27 02:21:19 +00:00
2017-11-28 21:07:14 +00:00
RouteDefinition(this.segments);
2017-11-27 02:21:19 +00:00
2019-02-03 16:44:44 +00:00
Parser<RouteResult> compile() {
Parser<RouteResult> out;
2017-11-27 02:21:19 +00:00
2017-11-28 21:07:14 +00:00
for (int i = 0; i < segments.length; i++) {
var s = segments[i];
bool isLast = i == segments.length - 1;
2019-11-28 17:40:32 +00:00
if (out == null) {
2017-11-28 21:07:14 +00:00
out = s.compile(isLast);
2019-11-28 17:40:32 +00:00
} else {
2018-08-20 18:59:51 +00:00
out = s.compileNext(
2019-02-03 16:44:44 +00:00
out.then(match('/')).index(0).cast<RouteResult>(), isLast);
2019-11-28 17:40:32 +00:00
}
2017-11-27 02:21:19 +00:00
}
return out;
}
}
2017-11-28 21:07:14 +00:00
abstract class RouteSegment {
2019-02-03 16:44:44 +00:00
Parser<RouteResult> compile(bool isLast);
2018-08-20 14:24:11 +00:00
2019-02-03 16:44:44 +00:00
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast);
2017-11-27 02:21:19 +00:00
}
2019-02-03 05:33:47 +00:00
class SlashSegment implements RouteSegment {
static final RegExp rgx = RegExp(r'/+');
const SlashSegment();
@override
2019-02-03 16:44:44 +00:00
Parser<RouteResult> compile(bool isLast) {
return match(rgx).map((_) => RouteResult({}));
2019-02-03 05:33:47 +00:00
}
@override
2019-02-03 16:44:44 +00:00
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
return p.then(compile(isLast)).index(0).cast<RouteResult>();
2019-02-03 05:33:47 +00:00
}
@override
String toString() => 'Slash';
}
2017-11-28 21:07:14 +00:00
class ConstantSegment extends RouteSegment {
2017-11-27 02:21:19 +00:00
final String text;
2017-11-28 21:07:14 +00:00
ConstantSegment(this.text);
2017-11-27 02:21:19 +00:00
@override
String toString() {
return 'Constant: $text';
}
@override
2019-02-03 16:44:44 +00:00
Parser<RouteResult> compile(bool isLast) {
return match(text).map((r) => RouteResult({}));
2017-11-27 02:21:19 +00:00
}
@override
2019-02-03 16:44:44 +00:00
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
return p.then(compile(isLast)).index(0).cast<RouteResult>();
2017-11-27 02:21:19 +00:00
}
}
2017-11-28 21:07:14 +00:00
class WildcardSegment extends RouteSegment {
2019-02-03 05:33:47 +00:00
final String pre, post;
WildcardSegment(this.pre, this.post);
2017-11-27 02:21:19 +00:00
@override
String toString() {
return 'Wildcard segment';
}
2019-02-03 05:33:47 +00:00
String _symbol(bool isLast) {
if (isLast) return r'.*';
return r'[^/]*';
}
2019-02-03 16:44:44 +00:00
RegExp _compile(bool isLast) {
return RegExp('$pre(${_symbol(isLast)})$post');
2019-11-28 17:40:32 +00:00
// if (isLast) return match(RegExp(r'.*'));
// return match(RegExp(r'[^/]*'));
2017-11-27 02:21:19 +00:00
}
@override
2019-02-03 16:44:44 +00:00
Parser<RouteResult> compile(bool isLast) {
return match(_compile(isLast))
.map((r) => RouteResult({}, tail: r.scanner.lastMatch[1]));
2017-11-27 02:21:19 +00:00
}
@override
2019-02-03 16:44:44 +00:00
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
return p.then(compile(isLast)).map((r) {
var items = r.value.cast<RouteResult>();
var a = items[0], b = items[1];
return a
..addAll(b?.params ?? {})
.._setTail(b?.tail);
});
2017-11-27 02:21:19 +00:00
}
}
2017-11-28 21:07:14 +00:00
class OptionalSegment extends ParameterSegment {
final ParameterSegment parameter;
2017-11-27 02:21:19 +00:00
2017-11-28 21:07:14 +00:00
OptionalSegment(this.parameter) : super(parameter.name, parameter.regExp);
2017-11-27 02:21:19 +00:00
@override
String toString() {
return 'Optional: $parameter';
}
@override
2019-02-03 16:44:44 +00:00
Parser<RouteResult> compile(bool isLast) {
2017-11-28 21:07:14 +00:00
return super.compile(isLast).opt();
2017-11-27 02:21:19 +00:00
}
@override
2019-02-03 16:44:44 +00:00
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
2017-11-27 02:21:19 +00:00
return p.then(_compile().opt()).map((r) {
2019-02-03 16:44:44 +00:00
if (r.value[1] == null) return r.value[0] as RouteResult;
return (r.value[0] as RouteResult)
2018-08-20 18:59:51 +00:00
..addAll({name: Uri.decodeComponent(r.value[1] as String)});
2017-11-27 02:21:19 +00:00
});
}
}
2017-11-28 21:07:14 +00:00
class ParameterSegment extends RouteSegment {
2017-11-27 02:21:19 +00:00
final String name;
final RegExp regExp;
2017-11-28 21:07:14 +00:00
ParameterSegment(this.name, this.regExp);
2017-11-27 02:21:19 +00:00
@override
String toString() {
if (regExp != null) return 'Param: $name (${regExp.pattern})';
return 'Param: $name';
}
2018-08-20 18:59:51 +00:00
Parser<String> _compile() {
2017-11-27 02:21:19 +00:00
return regExp != null
2019-02-03 05:33:47 +00:00
? match<String>(regExp).value((r) => r.scanner.lastMatch[1])
2017-11-28 21:07:14 +00:00
: RouteGrammar.notSlash;
2017-11-27 02:21:19 +00:00
}
@override
2019-02-03 16:44:44 +00:00
Parser<RouteResult> compile(bool isLast) {
2019-02-03 05:33:47 +00:00
return _compile()
2019-02-03 16:44:44 +00:00
.map((r) => RouteResult({name: Uri.decodeComponent(r.value)}));
2017-11-27 02:21:19 +00:00
}
@override
2019-02-03 16:44:44 +00:00
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
2017-11-27 02:21:19 +00:00
return p.then(_compile()).map((r) {
2019-02-03 16:44:44 +00:00
return (r.value[0] as RouteResult)
2018-08-20 18:59:51 +00:00
..addAll({name: Uri.decodeComponent(r.value[1] as String)});
2017-11-27 02:21:19 +00:00
});
}
}
2018-08-20 14:24:11 +00:00
class ParsedParameterSegment extends RouteSegment {
final String type;
final ParameterSegment parameter;
ParsedParameterSegment(this.type, this.parameter);
num getValue(String s) {
switch (type) {
case 'int':
return int.parse(s);
case 'double':
return double.parse(s);
default:
return num.parse(s);
}
}
@override
2019-02-03 16:44:44 +00:00
Parser<RouteResult> compile(bool isLast) {
return parameter._compile().map((r) => RouteResult(
{parameter.name: getValue(Uri.decodeComponent(r.span.text))}));
2018-08-20 14:24:11 +00:00
}
@override
2019-02-03 16:44:44 +00:00
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
2018-08-20 14:24:11 +00:00
return p.then(parameter._compile()).map((r) {
2019-02-03 16:44:44 +00:00
return (r.value[0] as RouteResult)
2018-08-20 18:59:51 +00:00
..addAll({
parameter.name: getValue(Uri.decodeComponent(r.value[1] as String))
});
2018-08-20 14:24:11 +00:00
});
}
}