platform/lib/src/grammar.dart
2018-08-20 15:33:06 -04:00

220 lines
5.9 KiB
Dart

part of angel_route.src.router;
class RouteGrammar {
static final RegExp rgx = new RegExp(r'\((.+)\)');
static final Parser<String> notSlash =
match<String>(new RegExp(r'[^/]+')).value((r) => r.span.text);
static final Parser<RegExp> regExp = match<RegExp>(new RegExp(r'\((.+)\)'))
.value((r) => new RegExp(r.scanner.lastMatch[1]));
static final Parser<String> parameterName =
match<String>(new RegExp(r':([A-Za-z0-9_]+)'))
.value((r) => r.span.text.substring(1));
static final Parser<ParameterSegment> parameterSegment = chain([
parameterName,
match<bool>('?').value((r) => true).opt(),
regExp.opt(),
]).map((r) {
var s = new ParameterSegment(r.value[0] as String, r.value[2] as RegExp);
return r.value[1] == true ? new OptionalSegment(s) : s;
});
static final Parser<ParsedParameterSegment> parsedParameterSegment = chain([
match(new RegExp(r'(int|num|double)'),
errorMessage: 'Expected "int","double", or "num".')
.map((r) => r.span.text),
parameterSegment,
]).map((r) {
return new ParsedParameterSegment(
r.value[0] as String, r.value[1] as ParameterSegment);
});
static final Parser<WildcardSegment> wildcardSegment =
match<WildcardSegment>('*').value((r) => new WildcardSegment());
static final Parser<ConstantSegment> constantSegment =
notSlash.map<ConstantSegment>((r) => new ConstantSegment(r.value));
static final Parser<RouteSegment> routeSegment = any<RouteSegment>([
parsedParameterSegment,
parameterSegment,
wildcardSegment,
constantSegment
]);
static final Parser<RouteDefinition> routeDefinition = routeSegment
.separatedBy(match('/'))
.map<RouteDefinition>((r) => new RouteDefinition(r.value ?? []))
.surroundedBy(match('/').star().opt());
}
class RouteDefinition {
final List<RouteSegment> segments;
RouteDefinition(this.segments);
Parser<Map<String, dynamic>> compile() {
Parser<Map<String, dynamic>> out;
for (int i = 0; i < segments.length; i++) {
var s = segments[i];
bool isLast = i == segments.length - 1;
if (out == null)
out = s.compile(isLast);
else
out = s.compileNext(
out.then(match('/')).index(0).cast<Map<String, dynamic>>(), isLast);
}
return out;
}
}
abstract class RouteSegment {
Parser<Map<String, dynamic>> compile(bool isLast);
Parser<Map<String, dynamic>> compileNext(
Parser<Map<String, dynamic>> p, bool isLast);
}
class ConstantSegment extends RouteSegment {
final String text;
ConstantSegment(this.text);
@override
String toString() {
return 'Constant: $text';
}
@override
Parser<Map<String, dynamic>> compile(bool isLast) {
return match<Map<String, dynamic>>(text).value((r) => <String, dynamic>{});
}
@override
Parser<Map<String, dynamic>> compileNext(
Parser<Map<String, dynamic>> p, bool isLast) {
return p.then(compile(isLast)).index(0).cast<Map<String, dynamic>>();
}
}
class WildcardSegment extends RouteSegment {
@override
String toString() {
return 'Wildcard segment';
}
Parser<Map<String, dynamic>> _compile(bool isLast) {
if (isLast) return match(new RegExp(r'.*'));
return match(new RegExp(r'[^/]*'));
}
@override
Parser<Map<String, dynamic>> compile(bool isLast) {
return _compile(isLast).map((r) => {});
}
@override
Parser<Map<String, dynamic>> compileNext(
Parser<Map<String, dynamic>> p, bool isLast) {
return p.then(_compile(isLast)).index(0).cast<Map<String, dynamic>>();
}
}
class OptionalSegment extends ParameterSegment {
final ParameterSegment parameter;
OptionalSegment(this.parameter) : super(parameter.name, parameter.regExp);
@override
String toString() {
return 'Optional: $parameter';
}
@override
Parser<Map<String, dynamic>> compile(bool isLast) {
return super.compile(isLast).opt();
}
@override
Parser<Map<String, dynamic>> compileNext(
Parser<Map<String, dynamic>> p, bool isLast) {
return p.then(_compile().opt()).map((r) {
if (r.value[1] == null) return r.value[0] as Map<String, dynamic>;
return (r.value[0] as Map<String, dynamic>)
..addAll({name: Uri.decodeComponent(r.value[1] as String)});
});
}
}
class ParameterSegment extends RouteSegment {
final String name;
final RegExp regExp;
ParameterSegment(this.name, this.regExp);
@override
String toString() {
if (regExp != null) return 'Param: $name (${regExp.pattern})';
return 'Param: $name';
}
Parser<String> _compile() {
return regExp != null
? match<String>(regExp).value((r) => r.span.text)
: RouteGrammar.notSlash;
}
@override
Parser<Map<String, dynamic>> compile(bool isLast) {
return _compile().map<Map<String, dynamic>>(
(r) => {name: Uri.decodeComponent(r.span.text)});
}
@override
Parser<Map<String, dynamic>> compileNext(
Parser<Map<String, dynamic>> p, bool isLast) {
return p.then(_compile()).map((r) {
return (r.value[0] as Map<String, dynamic>)
..addAll({name: Uri.decodeComponent(r.value[1] as String)});
});
}
}
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
Parser<Map<String, dynamic>> compile(bool isLast) {
return parameter._compile().map(
(r) => {parameter.name: getValue(Uri.decodeComponent(r.span.text))});
}
@override
Parser<Map<String, dynamic>> compileNext(
Parser<Map<String, dynamic>> p, bool isLast) {
return p.then(parameter._compile()).map((r) {
return (r.value[0] as Map<String, dynamic>)
..addAll({
parameter.name: getValue(Uri.decodeComponent(r.value[1] as String))
});
});
}
}