part of angel_route.src.router; class _RouteGrammar { static final Parser notSlash = match(new RegExp(r'[^/]+')).value((r) => r.span.text); static final Parser regExp = new _RegExpParser(); static final Parser parameterName = match(new RegExp(r':([A-Za-z0-9_]+)')) .value((r) => r.span.text.substring(1)); static final Parser<_ParameterSegment> parameterSegment = chain([ parameterName, match('?').value((r) => true).opt(), regExp.opt(), ]).map((r) { var s = new _ParameterSegment(r.value[0], r.value[2]); return r.value[1] == true ? new _OptionalSegment(s) : s; }); static final Parser<_WildcardSegment> wildcardSegment = match('*').value((r) => new _WildcardSegment()); static final Parser<_ConstantSegment> constantSegment = notSlash.map((r) => new _ConstantSegment(r.value)); static final Parser<_RouteSegment> routeSegment = any([parameterSegment, wildcardSegment, constantSegment]); static final Parser<_RouteDefinition> routeDefinition = routeSegment .separatedBy(match('/')) .map((r) => new _RouteDefinition(r.value ?? [])) .surroundedBy(match('/').star().opt()); } class _RegExpParser extends Parser { static final RegExp rgx = new RegExp(r'\((.+)\)'); @override ParseResult parse(SpanScanner scanner, [int depth = 1]) { if (!scanner.matches(rgx)) return new ParseResult(this, false, []); return new ParseResult(this, true, [], span: scanner.lastSpan, value: new RegExp(scanner.lastMatch[1])); } } class _RouteDefinition { final List<_RouteSegment> segments; _RouteDefinition(this.segments); Parser> compile() { Parser> out; for (var s in segments) { if (out == null) out = s.compile(); else out = s.compileNext(out.then(match('/')).index(0)); } return out; } } abstract class _RouteSegment { Parser> compile(); Parser> compileNext(Parser> p); } class _ConstantSegment extends _RouteSegment { final String text; _ConstantSegment(this.text); @override String toString() { return 'Constant: $text'; } @override Parser> compile() { return match(text).value((r) => {}); } @override Parser> compileNext(Parser> p) { return p.then(compile()).index(0); } } class _WildcardSegment extends _RouteSegment { @override String toString() { return 'Wildcard segment'; } Parser> _compile() { return match(new RegExp(r'[^/]*')); } @override Parser> compile() { return _compile().map((r) => {}); } @override Parser> compileNext(Parser> p) { return p.then(_compile()).index(0); } } class _OptionalSegment extends _ParameterSegment { final _ParameterSegment parameter; _OptionalSegment(this.parameter) : super(parameter.name, parameter.regExp); @override String toString() { return 'Optional: $parameter'; } @override Parser> compile() { return super.compile().opt(); } @override Parser> compileNext(Parser> p) { return p.then(_compile().opt()).map((r) { if (r.value[1] == null) return r.value[0]; return r.value[0]..addAll({name: Uri.decodeComponent(r.value[1])}); }); } } 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> _compile() { return regExp != null ? match(regExp).value((r) => r.span.text) : _RouteGrammar.notSlash; } @override Parser> compile() { return _compile().map((r) => {name: Uri.decodeComponent(r.span.text)}); } @override Parser> compileNext(Parser> p) { return p.then(_compile()).map((r) { return r.value[0]..addAll({name: Uri.decodeComponent(r.value[1])}); }); } }