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

Side by Side Diff: pkg/glob/lib/src/ast.dart

Issue 549633002: Add support for listing to the glob package. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Don't run glob tests on the browser. 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library glob.ast; 5 library glob.ast;
6 6
7 import 'package:path/path.dart' as p; 7 import 'package:path/path.dart' as p;
8 8
9 import 'utils.dart'; 9 import 'utils.dart';
10 10
11 const _SEPARATOR = 0x2F; // "/" 11 const _SEPARATOR = 0x2F; // "/"
12 12
13 /// A node in the abstract syntax tree for a glob. 13 /// A node in the abstract syntax tree for a glob.
14 abstract class AstNode { 14 abstract class AstNode {
15 /// The cached regular expression that this AST was compiled into. 15 /// The cached regular expression that this AST was compiled into.
16 RegExp _regExp; 16 RegExp _regExp;
17 17
18 /// Whether this glob could match an absolute path. 18 /// Whether this glob could match an absolute path.
19 /// 19 ///
20 /// Either this or [canMatchRelative] or both will be true. 20 /// Either this or [canMatchRelative] or both will be true.
21 final bool canMatchAbsolute = false; 21 final bool canMatchAbsolute = false;
22 22
23 /// Whether this glob could match a relative path. 23 /// Whether this glob could match a relative path.
24 /// 24 ///
25 /// Either this or [canMatchRelative] or both will be true. 25 /// Either this or [canMatchRelative] or both will be true.
26 final bool canMatchRelative = true; 26 final bool canMatchRelative = true;
27 27
28 /// Returns a new glob with all the options bubbled to the top level.
29 ///
30 /// In particular, this returns a glob AST with two guarantees:
31 ///
32 /// 1. There are no [OptionsNode]s other than the one at the top level.
33 /// 2. It matches the same set of paths as [this].
34 OptionsNode expand() => new OptionsNode([new SequenceNode([this])]);
Bob Nystrom 2014/09/18 22:17:39 How about "flattenOptions"? Using the same name as
nweiz 2014/09/22 23:48:39 Done.
35
28 /// Returns whether this glob matches [string]. 36 /// Returns whether this glob matches [string].
29 bool matches(String string) { 37 bool matches(String string) {
30 if (_regExp == null) _regExp = new RegExp('^${_toRegExp()}\$'); 38 if (_regExp == null) _regExp = new RegExp('^${_toRegExp()}\$');
31 return _regExp.hasMatch(string); 39 return _regExp.hasMatch(string);
32 } 40 }
33 41
34 /// Subclasses should override this to return a regular expression component. 42 /// Subclasses should override this to return a regular expression component.
35 String _toRegExp(); 43 String _toRegExp();
36 } 44 }
37 45
38 /// A sequence of adjacent AST nodes. 46 /// A sequence of adjacent AST nodes.
39 class SequenceNode extends AstNode { 47 class SequenceNode extends AstNode {
40 /// The nodes in the sequence. 48 /// The nodes in the sequence.
41 final List<AstNode> nodes; 49 final List<AstNode> nodes;
42 50
43 bool get canMatchAbsolute => nodes.first.canMatchAbsolute; 51 bool get canMatchAbsolute => nodes.first.canMatchAbsolute;
44 bool get canMatchRelative => nodes.first.canMatchRelative; 52 bool get canMatchRelative => nodes.first.canMatchRelative;
45 53
46 SequenceNode(Iterable<AstNode> nodes) 54 SequenceNode(Iterable<AstNode> nodes)
47 : nodes = nodes.toList(); 55 : nodes = nodes.toList();
48 56
57 OptionsNode expand() {
58 if (nodes.isEmpty) return new OptionsNode([this]);
59
60 var sequences = nodes.first.expand().options;
61 for (var node in nodes.skip(1)) {
62 var nextSequences = node.expand().options;
63 sequences = sequences.expand((sequence) {
64 return nextSequences.map((nextSequence) {
Bob Nystrom 2014/09/18 22:17:39 A loop containing an expand call that's mapping ea
nweiz 2014/09/22 23:48:39 Done.
65 var next = nextSequence.nodes.toList();
66 var combined = sequence.nodes.toList();
67
68 // Combine adjacent LiteralNodes.
69 if (!combined.isEmpty && !next.isEmpty &&
70 combined.last is LiteralNode && next.first is LiteralNode) {
71 combined[combined.length - 1] =
72 new LiteralNode(combined.last.text + next.removeAt(0).text);
73 }
74
75 return new SequenceNode(combined..addAll(next));
76 });
77 });
78 }
79 return new OptionsNode(sequences.toList());
80 }
81
82 /// Splits this glob into components along its path separators.
Bob Nystrom 2014/09/18 22:17:39 A before/after example would help a lot here.
nweiz 2014/09/22 23:48:39 Done.
83 ///
84 /// This should only be called once the glob has been [expand]ed. [context] is
Bob Nystrom 2014/09/18 22:17:39 If this is only valid on certain states, maybe it
nweiz 2014/09/22 23:48:39 Rather than doing that (which I think would add a
85 /// used to determine what absolute roots look like for this glob.
86 List<SequenceNode> split(p.Context context) {
87 // The inner list represents a [SequenceNode] for a single path component;
88 // the outer list is the list of components.
89 var sequences = [[]];
Bob Nystrom 2014/09/18 22:17:39 I think this code will be easier to read if you ad
nweiz 2014/09/22 23:48:39 Done.
90
91 for (var node in nodes) {
92 // This should only be called after [expand].
93 assert(node is! OptionsNode);
94
95 if (node is! LiteralNode || !node.text.contains('/')) {
96 sequences.last.add(node);
97 continue;
98 }
99
100 var text = node.text;
101 if (context.style == p.Style.windows) text.replaceAll("/", "\\");
102 var components = context.split(text);
Bob Nystrom 2014/09/18 22:17:39 "nodeComponents"?
nweiz 2014/09/22 23:48:39 This is referenced a lot more often than the previ
103
104 // If the first component is absolute, that means it's a separator (on
105 // Windows some non-separator things are also absolute, but it's invalid
106 // to have "C:" show up in the middle of a path anyway).
107 if (context.isAbsolute(components.first)) {
108 // If this is the first component, it's the root.
109 if (sequences.length == 1 && sequences.first.isEmpty) {
110 var root = components.first;
111 if (context.style == p.Style.windows) {
112 root = root.replaceAll("\\", "/");
Bob Nystrom 2014/09/18 22:17:39 Why switch to forward slashes on Windows?
nweiz 2014/09/22 23:48:39 Above, we switched to backslashes to make [context
113 }
114 sequences.last.add(new LiteralNode(root));
115 }
116 sequences.add([]);
117 components = components.skip(1);
118 if (components.isEmpty) continue;
119 }
120
121 // For each component except the last one, add a separate sequence to
122 // [sequences] containing only that component.
123 for (var component in components.take(components.length - 1)) {
124 sequences.last.add(new LiteralNode(component));
125 sequences.add([]);
126 }
127
128 // For the final component, only end its sequence (by adding a new empty
129 // sequence) if it ends with a separator.
130 sequences.last.add(new LiteralNode(components.last));
131 if (node.text.endsWith('/')) sequences.add([]);
132 }
133
134 if (sequences.last.isEmpty) sequences.removeLast();
135 return sequences.map((sequence) => new SequenceNode(sequence)).toList();
136 }
137
49 String _toRegExp() => nodes.map((node) => node._toRegExp()).join(); 138 String _toRegExp() => nodes.map((node) => node._toRegExp()).join();
50 139
51 String toString() => nodes.join(); 140 String toString() => nodes.join();
52 } 141 }
53 142
54 /// A node matching zero or more non-separator characters. 143 /// A node matching zero or more non-separator characters.
55 class StarNode extends AstNode { 144 class StarNode extends AstNode {
56 StarNode(); 145 StarNode();
57 146
58 String _toRegExp() => '[^/]*'; 147 String _toRegExp() => '[^/]*';
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
112 /// 201 ///
113 /// The ends of the ranges are unicode code points. 202 /// The ends of the ranges are unicode code points.
114 final Set<Range> ranges; 203 final Set<Range> ranges;
115 204
116 /// Whether this range was negated. 205 /// Whether this range was negated.
117 final bool negated; 206 final bool negated;
118 207
119 RangeNode(Iterable<Range> ranges, {this.negated}) 208 RangeNode(Iterable<Range> ranges, {this.negated})
120 : ranges = ranges.toSet(); 209 : ranges = ranges.toSet();
121 210
211 OptionsNode expand() {
212 if (negated || ranges.any((range) => !range.isSingleton)) {
213 return super.expand();
214 }
215
216 // If a range explicitly lists a set of characters, return each character as
217 // a separate expansion.
218 return new OptionsNode(ranges.map((range) {
219 return new SequenceNode([
220 new LiteralNode(new String.fromCharCodes([range.min]))
221 ]);
222 }));
223 }
224
122 String _toRegExp() { 225 String _toRegExp() {
123 var buffer = new StringBuffer(); 226 var buffer = new StringBuffer();
124 227
125 var containsSeparator = ranges.any((range) => range.contains(_SEPARATOR)); 228 var containsSeparator = ranges.any((range) => range.contains(_SEPARATOR));
126 if (!negated && containsSeparator) { 229 if (!negated && containsSeparator) {
127 // Add `(?!/)` because ranges are never allowed to match separators. 230 // Add `(?!/)` because ranges are never allowed to match separators.
128 buffer.write('(?!/)'); 231 buffer.write('(?!/)');
129 } 232 }
130 233
131 buffer.write('['); 234 buffer.write('[');
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 class OptionsNode extends AstNode { 268 class OptionsNode extends AstNode {
166 /// The options to match. 269 /// The options to match.
167 final List<SequenceNode> options; 270 final List<SequenceNode> options;
168 271
169 bool get canMatchAbsolute => options.any((node) => node.canMatchAbsolute); 272 bool get canMatchAbsolute => options.any((node) => node.canMatchAbsolute);
170 bool get canMatchRelative => options.any((node) => node.canMatchRelative); 273 bool get canMatchRelative => options.any((node) => node.canMatchRelative);
171 274
172 OptionsNode(Iterable<SequenceNode> options) 275 OptionsNode(Iterable<SequenceNode> options)
173 : options = options.toList(); 276 : options = options.toList();
174 277
278 OptionsNode expand() =>
279 new OptionsNode(options.expand((option) => option.expand().options));
280
175 String _toRegExp() => 281 String _toRegExp() =>
176 '(?:${options.map((option) => option._toRegExp()).join("|")})'; 282 '(?:${options.map((option) => option._toRegExp()).join("|")})';
177 283
178 String toString() => '{${options.join(',')}}'; 284 String toString() => '{${options.join(',')}}';
179 } 285 }
180 286
181 /// A node that matches a literal string. 287 /// A node that matches a literal string.
182 class LiteralNode extends AstNode { 288 class LiteralNode extends AstNode {
183 /// The string to match. 289 /// The string to match.
184 final String text; 290 final String text;
185 291
186 /// The path context for the glob. 292 /// The path context for the glob.
187 /// 293 ///
188 /// This is used to determine whether this could match an absolute path. 294 /// This is used to determine whether this could match an absolute path.
189 final p.Context _context; 295 final p.Context _context;
190 296
191 bool get canMatchAbsolute { 297 bool get canMatchAbsolute {
192 var nativeText = _context.style == p.Style.windows ? 298 var nativeText = _context.style == p.Style.windows ?
193 text.replaceAll('/', '\\') : text; 299 text.replaceAll('/', '\\') : text;
194 return _context.isAbsolute(nativeText); 300 return _context.isAbsolute(nativeText);
195 } 301 }
196 302
197 bool get canMatchRelative => !canMatchAbsolute; 303 bool get canMatchRelative => !canMatchAbsolute;
198 304
199 LiteralNode(this.text, this._context); 305 LiteralNode(this.text, [this._context]);
200 306
201 String _toRegExp() => regExpQuote(text); 307 String _toRegExp() => regExpQuote(text);
202 308
203 String toString() => text; 309 String toString() => text;
204 } 310 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698