Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(48)

Unified Diff: pkg/glob/lib/src/ast.dart

Issue 506993004: Create a glob package. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Code review changes Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/glob/lib/glob.dart ('k') | pkg/glob/lib/src/parser.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/glob/lib/src/ast.dart
diff --git a/pkg/glob/lib/src/ast.dart b/pkg/glob/lib/src/ast.dart
new file mode 100644
index 0000000000000000000000000000000000000000..5e1da1a72c2fddff8fac2c64c6f3d132d8c0c0f9
--- /dev/null
+++ b/pkg/glob/lib/src/ast.dart
@@ -0,0 +1,204 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library glob.ast;
+
+import 'package:path/path.dart' as p;
+
+import 'utils.dart';
+
+const _SEPARATOR = 0x2F; // "/"
+
+/// A node in the abstract syntax tree for a glob.
+abstract class AstNode {
+ /// The cached regular expression that this AST was compiled into.
+ RegExp _regExp;
+
+ /// Whether this glob could match an absolute path.
+ ///
+ /// Either this or [canMatchRelative] or both will be true.
+ final bool canMatchAbsolute = false;
+
+ /// Whether this glob could match a relative path.
+ ///
+ /// Either this or [canMatchRelative] or both will be true.
+ final bool canMatchRelative = true;
+
+ /// Returns whether this glob matches [string].
+ bool matches(String string) {
+ if (_regExp == null) _regExp = new RegExp('^${_toRegExp()}\$');
+ return _regExp.hasMatch(string);
+ }
+
+ /// Subclasses should override this to return a regular expression component.
+ String _toRegExp();
+}
+
+/// A sequence of adjacent AST nodes.
+class SequenceNode extends AstNode {
+ /// The nodes in the sequence.
+ final List<AstNode> nodes;
+
+ bool get canMatchAbsolute => nodes.first.canMatchAbsolute;
+ bool get canMatchRelative => nodes.first.canMatchRelative;
+
+ SequenceNode(Iterable<AstNode> nodes)
+ : nodes = nodes.toList();
+
+ String _toRegExp() => nodes.map((node) => node._toRegExp()).join();
+
+ String toString() => nodes.join();
+}
+
+/// A node matching zero or more non-separator characters.
+class StarNode extends AstNode {
+ StarNode();
+
+ String _toRegExp() => '[^/]*';
+
+ String toString() => '*';
+}
+
+/// A node matching zero or more characters that may be separators.
+class DoubleStarNode extends AstNode {
+ /// The path context for the glob.
+ ///
+ /// This is used to determine what absolute paths look like.
+ final p.Context _context;
+
+ DoubleStarNode(this._context);
+
+ String _toRegExp() {
+ // Double star shouldn't match paths with a leading "../", since these paths
+ // wouldn't be listed with this glob. We only check for "../" at the
+ // beginning since the paths are normalized before being checked against the
+ // glob.
+ var buffer = new StringBuffer()..write(r'(?!^(?:\.\./|');
+
+ // A double star at the beginning of the glob also shouldn't match absolute
+ // paths, since those also wouldn't be listed. Which root patterns we look
+ // for depends on the style of path we're matching.
+ if (_context.style == p.Style.posix) {
+ buffer.write(r'/');
+ } else if (_context.style == p.Style.windows) {
+ buffer.write(r'//|[A-Za-z]:/');
+ } else {
+ assert(_context.style == p.Style.url);
+ buffer.write(r'[a-zA-Z][-+.a-zA-Z\d]*://|/');
+ }
+
+ // Use `[^]` rather than `.` so that it matches newlines as well.
+ buffer.write(r'))[^]*');
+
+ return buffer.toString();
+ }
+
+ String toString() => '**';
+}
+
+/// A node matching a single non-separator character.
+class AnyCharNode extends AstNode {
+ AnyCharNode();
+
+ String _toRegExp() => '[^/]';
+
+ String toString() => '?';
+}
+
+/// A node matching a single character in a range of options.
+class RangeNode extends AstNode {
+ /// The ranges matched by this node.
+ ///
+ /// The ends of the ranges are unicode code points.
+ final Set<Range> ranges;
+
+ /// Whether this range was negated.
+ final bool negated;
+
+ RangeNode(Iterable<Range> ranges, {this.negated})
+ : ranges = ranges.toSet();
+
+ String _toRegExp() {
+ var buffer = new StringBuffer();
+
+ var containsSeparator = ranges.any((range) => range.contains(_SEPARATOR));
+ if (!negated && containsSeparator) {
+ // Add `(?!/)` because ranges are never allowed to match separators.
+ buffer.write('(?!/)');
+ }
+
+ buffer.write('[');
+ if (negated) {
+ buffer.write('^');
+ // If the range doesn't itself exclude separators, exclude them ourselves,
+ // since ranges are never allowed to match them.
+ if (!containsSeparator) buffer.write('/');
+ }
+
+ for (var range in ranges) {
+ var start = new String.fromCharCodes([range.min]);
+ buffer.write(regExpQuote(start));
+ if (range.isSingleton) continue;
+ buffer.write('-');
+ buffer.write(regExpQuote(new String.fromCharCodes([range.max])));
+ }
+
+ buffer.write(']');
+ return buffer.toString();
+ }
+
+ String toString() {
+ var buffer = new StringBuffer()..write('[');
+ for (var range in ranges) {
+ buffer.writeCharCode(range.min);
+ if (range.isSingleton) continue;
+ buffer.write('-');
+ buffer.writeCharCode(range.max);
+ }
+ buffer.write(']');
+ return buffer.toString();
+ }
+}
+
+/// A node that matches one of several options.
+class OptionsNode extends AstNode {
+ /// The options to match.
+ final List<SequenceNode> options;
+
+ bool get canMatchAbsolute => options.any((node) => node.canMatchAbsolute);
+ bool get canMatchRelative => options.any((node) => node.canMatchRelative);
+
+ OptionsNode(Iterable<SequenceNode> options)
+ : options = options.toList();
+
+ String _toRegExp() =>
+ '(?:${options.map((option) => option._toRegExp()).join("|")})';
+
+ String toString() => '{${options.join(',')}}';
+}
+
+/// A node that matches a literal string.
+class LiteralNode extends AstNode {
+ /// The string to match.
+ final String text;
+
+ /// The path context for the glob.
+ ///
+ /// This is used to determine whether this could match an absolute path.
+ final p.Context _context;
+
+ bool get canMatchAbsolute {
+ var nativeText = _context.style == p.Style.windows ?
+ text.replaceAll('/', '\\') : text;
+ return _context.isAbsolute(nativeText);
+ }
+
+ bool get canMatchRelative => !canMatchAbsolute;
+
+ LiteralNode(this.text, this._context);
+
+ String _toRegExp() => regExpQuote(text);
+
+ String toString() => text;
+}
« no previous file with comments | « pkg/glob/lib/glob.dart ('k') | pkg/glob/lib/src/parser.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698