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

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

Issue 2989763002: Update charted to 0.4.8 and roll (Closed)
Patch Set: Removed Cutch from list of reviewers Created 3 years, 4 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
« no previous file with comments | « packages/glob/lib/glob.dart ('k') | packages/glob/lib/src/list_tree.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
6
7 import 'package:path/path.dart' as p; 5 import 'package:path/path.dart' as p;
8 import 'package:collection/collection.dart'; 6 import 'package:collection/collection.dart';
9 7
10 import 'utils.dart'; 8 import 'utils.dart';
11 9
12 const _SEPARATOR = 0x2F; // "/" 10 const _SEPARATOR = 0x2F; // "/"
13 11
14 /// A node in the abstract syntax tree for a glob. 12 /// A node in the abstract syntax tree for a glob.
15 abstract class AstNode { 13 abstract class AstNode {
16 /// The cached regular expression that this AST was compiled into. 14 /// The cached regular expression that this AST was compiled into.
17 RegExp _regExp; 15 RegExp _regExp;
18 16
17 /// Whether this node matches case-sensitively or not.
18 final bool caseSensitive;
19
19 /// Whether this glob could match an absolute path. 20 /// Whether this glob could match an absolute path.
20 /// 21 ///
21 /// Either this or [canMatchRelative] or both will be true. 22 /// Either this or [canMatchRelative] or both will be true.
22 final bool canMatchAbsolute = false; 23 bool get canMatchAbsolute => false;
23 24
24 /// Whether this glob could match a relative path. 25 /// Whether this glob could match a relative path.
25 /// 26 ///
26 /// Either this or [canMatchRelative] or both will be true. 27 /// Either this or [canMatchRelative] or both will be true.
27 final bool canMatchRelative = true; 28 bool get canMatchRelative => true;
29
30 AstNode._(this.caseSensitive);
28 31
29 /// Returns a new glob with all the options bubbled to the top level. 32 /// Returns a new glob with all the options bubbled to the top level.
30 /// 33 ///
31 /// In particular, this returns a glob AST with two guarantees: 34 /// In particular, this returns a glob AST with two guarantees:
32 /// 35 ///
33 /// 1. There are no [OptionsNode]s other than the one at the top level. 36 /// 1. There are no [OptionsNode]s other than the one at the top level.
34 /// 2. It matches the same set of paths as [this]. 37 /// 2. It matches the same set of paths as [this].
35 /// 38 ///
36 /// For example, given the glob `{foo,bar}/{click/clack}`, this would return 39 /// For example, given the glob `{foo,bar}/{click/clack}`, this would return
37 /// `{foo/click,foo/clack,bar/click,bar/clack}`. 40 /// `{foo/click,foo/clack,bar/click,bar/clack}`.
38 OptionsNode flattenOptions() => new OptionsNode([new SequenceNode([this])]); 41 OptionsNode flattenOptions() => new OptionsNode(
42 [new SequenceNode([this], caseSensitive: caseSensitive)],
43 caseSensitive: caseSensitive);
39 44
40 /// Returns whether this glob matches [string]. 45 /// Returns whether this glob matches [string].
41 bool matches(String string) { 46 bool matches(String string) {
42 if (_regExp == null) _regExp = new RegExp('^${_toRegExp()}\$'); 47 if (_regExp == null) {
48 _regExp = new RegExp('^${_toRegExp()}\$', caseSensitive: caseSensitive);
49 }
43 return _regExp.hasMatch(string); 50 return _regExp.hasMatch(string);
44 } 51 }
45 52
46 /// Subclasses should override this to return a regular expression component. 53 /// Subclasses should override this to return a regular expression component.
47 String _toRegExp(); 54 String _toRegExp();
48 } 55 }
49 56
50 /// A sequence of adjacent AST nodes. 57 /// A sequence of adjacent AST nodes.
51 class SequenceNode extends AstNode { 58 class SequenceNode extends AstNode {
52 /// The nodes in the sequence. 59 /// The nodes in the sequence.
53 final List<AstNode> nodes; 60 final List<AstNode> nodes;
54 61
55 bool get canMatchAbsolute => nodes.first.canMatchAbsolute; 62 bool get canMatchAbsolute => nodes.first.canMatchAbsolute;
56 bool get canMatchRelative => nodes.first.canMatchRelative; 63 bool get canMatchRelative => nodes.first.canMatchRelative;
57 64
58 SequenceNode(Iterable<AstNode> nodes) 65 SequenceNode(Iterable<AstNode> nodes, {bool caseSensitive: true})
59 : nodes = nodes.toList(); 66 : nodes = nodes.toList(),
67 super._(caseSensitive);
60 68
61 OptionsNode flattenOptions() { 69 OptionsNode flattenOptions() {
62 if (nodes.isEmpty) return new OptionsNode([this]); 70 if (nodes.isEmpty) {
71 return new OptionsNode([this], caseSensitive: caseSensitive);
72 }
63 73
64 var sequences = nodes.first.flattenOptions().options 74 var sequences = nodes.first.flattenOptions().options
65 .map((sequence) => sequence.nodes); 75 .map((sequence) => sequence.nodes);
66 for (var node in nodes.skip(1)) { 76 for (var node in nodes.skip(1)) {
67 // Concatenate all sequences in the next options node ([nextSequences]) 77 // Concatenate all sequences in the next options node ([nextSequences])
68 // onto all previous sequences ([sequences]). 78 // onto all previous sequences ([sequences]).
69 var nextSequences = node.flattenOptions().options; 79 var nextSequences = node.flattenOptions().options;
70 sequences = sequences.expand((sequence) { 80 sequences = sequences.expand((sequence) {
71 return nextSequences.map((nextSequence) { 81 return nextSequences.map((nextSequence) {
72 return sequence.toList()..addAll(nextSequence.nodes); 82 return sequence.toList()..addAll(nextSequence.nodes);
73 }); 83 });
74 }); 84 });
75 } 85 }
76 86
77 return new OptionsNode(sequences.map((sequence) { 87 return new OptionsNode(sequences.map((sequence) {
78 // Combine any adjacent LiteralNodes in [sequence]. 88 // Combine any adjacent LiteralNodes in [sequence].
79 return new SequenceNode(sequence.fold([], (combined, node) { 89 return new SequenceNode(sequence.fold/*<List<AstNode>>*/([], (combined, no de) {
80 if (combined.isEmpty || combined.last is! LiteralNode || 90 if (combined.isEmpty || combined.last is! LiteralNode ||
81 node is! LiteralNode) { 91 node is! LiteralNode) {
82 return combined..add(node); 92 return combined..add(node);
83 } 93 }
84 94
85 combined[combined.length - 1] = 95 combined[combined.length - 1] = new LiteralNode(
86 new LiteralNode(combined.last.text + node.text); 96 // TODO(nweiz): Avoid casting when sdk#25565 is fixed.
97 (combined.last as LiteralNode).text + (node as LiteralNode).text,
98 caseSensitive: caseSensitive);
87 return combined; 99 return combined;
88 })); 100 }), caseSensitive: caseSensitive);
89 })); 101 }), caseSensitive: caseSensitive);
90 } 102 }
91 103
92 /// Splits this glob into components along its path separators. 104 /// Splits this glob into components along its path separators.
93 /// 105 ///
94 /// For example, given the glob `foo/*/*.dart`, this would return three globs: 106 /// For example, given the glob `foo/*/*.dart`, this would return three globs:
95 /// `foo`, `*`, and `*.dart`. 107 /// `foo`, `*`, and `*.dart`.
96 /// 108 ///
97 /// Path separators within options nodes are not split. For example, 109 /// Path separators within options nodes are not split. For example,
98 /// `foo/{bar,baz/bang}/qux` will return three globs: `foo`, `{bar,baz/bang}`, 110 /// `foo/{bar,baz/bang}/qux` will return three globs: `foo`, `{bar,baz/bang}`,
99 /// and `qux`. 111 /// and `qux`.
100 /// 112 ///
101 /// [context] is used to determine what absolute roots look like for this 113 /// [context] is used to determine what absolute roots look like for this
102 /// glob. 114 /// glob.
103 List<SequenceNode> split(p.Context context) { 115 List<SequenceNode> split(p.Context context) {
104 var componentsToReturn = []; 116 var componentsToReturn = <SequenceNode>[];
105 var currentComponent; 117 List<AstNode> currentComponent;
106 118
107 addNode(node) { 119 addNode(AstNode node) {
108 if (currentComponent == null) currentComponent = []; 120 if (currentComponent == null) currentComponent = [];
109 currentComponent.add(node); 121 currentComponent.add(node);
110 } 122 }
111 123
112 finishComponent() { 124 finishComponent() {
113 if (currentComponent == null) return; 125 if (currentComponent == null) return;
114 componentsToReturn.add(new SequenceNode(currentComponent)); 126 componentsToReturn.add(
127 new SequenceNode(currentComponent, caseSensitive: caseSensitive));
115 currentComponent = null; 128 currentComponent = null;
116 } 129 }
117 130
118 for (var node in nodes) { 131 for (var node in nodes) {
119 if (node is! LiteralNode || !node.text.contains('/')) { 132 if (node is! LiteralNode) {
120 addNode(node); 133 addNode(node);
121 continue; 134 continue;
122 } 135 }
123 136
124 var text = node.text; 137 // TODO(nweiz): Avoid casting when sdk#25565 is fixed.
138 var literal = node as LiteralNode;
139 if (!literal.text.contains('/')) {
140 addNode(literal);
141 continue;
142 }
143
144 var text = literal.text;
125 if (context.style == p.Style.windows) text = text.replaceAll("/", "\\"); 145 if (context.style == p.Style.windows) text = text.replaceAll("/", "\\");
126 var components = context.split(text); 146 var components = context.split(text);
127 147
128 // If the first component is absolute, that means it's a separator (on 148 // If the first component is absolute, that means it's a separator (on
129 // Windows some non-separator things are also absolute, but it's invalid 149 // Windows some non-separator things are also absolute, but it's invalid
130 // to have "C:" show up in the middle of a path anyway). 150 // to have "C:" show up in the middle of a path anyway).
131 if (context.isAbsolute(components.first)) { 151 if (context.isAbsolute(components.first)) {
132 // If this is the first component, it's the root. 152 // If this is the first component, it's the root.
133 if (componentsToReturn.isEmpty && currentComponent == null) { 153 if (componentsToReturn.isEmpty && currentComponent == null) {
134 var root = components.first; 154 var root = components.first;
135 if (context.style == p.Style.windows) { 155 if (context.style == p.Style.windows) {
136 // Above, we switched to backslashes to make [context.split] handle 156 // Above, we switched to backslashes to make [context.split] handle
137 // roots properly. That means that if there is a root, it'll still 157 // roots properly. That means that if there is a root, it'll still
138 // have backslashes, where forward slashes are required for globs. 158 // have backslashes, where forward slashes are required for globs.
139 // So we switch it back here. 159 // So we switch it back here.
140 root = root.replaceAll("\\", "/"); 160 root = root.replaceAll("\\", "/");
141 } 161 }
142 addNode(new LiteralNode(root)); 162 addNode(new LiteralNode(root, caseSensitive: caseSensitive));
143 } 163 }
144 finishComponent(); 164 finishComponent();
145 components = components.skip(1); 165 components = components.skip(1);
146 if (components.isEmpty) continue; 166 if (components.isEmpty) continue;
147 } 167 }
148 168
149 // For each component except the last one, add a separate sequence to 169 // For each component except the last one, add a separate sequence to
150 // [sequences] containing only that component. 170 // [sequences] containing only that component.
151 for (var component in components.take(components.length - 1)) { 171 for (var component in components.take(components.length - 1)) {
152 addNode(new LiteralNode(component)); 172 addNode(new LiteralNode(component, caseSensitive: caseSensitive));
153 finishComponent(); 173 finishComponent();
154 } 174 }
155 175
156 // For the final component, only end its sequence (by adding a new empty 176 // For the final component, only end its sequence (by adding a new empty
157 // sequence) if it ends with a separator. 177 // sequence) if it ends with a separator.
158 addNode(new LiteralNode(components.last)); 178 addNode(new LiteralNode(components.last, caseSensitive: caseSensitive));
159 if (node.text.endsWith('/')) finishComponent(); 179 if (literal.text.endsWith('/')) finishComponent();
160 } 180 }
161 181
162 finishComponent(); 182 finishComponent();
163 return componentsToReturn; 183 return componentsToReturn;
164 } 184 }
165 185
166 String _toRegExp() => nodes.map((node) => node._toRegExp()).join(); 186 String _toRegExp() => nodes.map((node) => node._toRegExp()).join();
167 187
168 bool operator==(Object other) => other is SequenceNode && 188 bool operator==(Object other) => other is SequenceNode &&
169 const IterableEquality().equals(nodes, other.nodes); 189 const IterableEquality().equals(nodes, other.nodes);
170 190
171 int get hashCode => const IterableEquality().hash(nodes); 191 int get hashCode => const IterableEquality().hash(nodes);
172 192
173 String toString() => nodes.join(); 193 String toString() => nodes.join();
174 } 194 }
175 195
176 /// A node matching zero or more non-separator characters. 196 /// A node matching zero or more non-separator characters.
177 class StarNode extends AstNode { 197 class StarNode extends AstNode {
178 StarNode(); 198 StarNode({bool caseSensitive: true}) : super._(caseSensitive);
179 199
180 String _toRegExp() => '[^/]*'; 200 String _toRegExp() => '[^/]*';
181 201
182 bool operator==(Object other) => other is StarNode; 202 bool operator==(Object other) => other is StarNode;
183 203
184 int get hashCode => 0; 204 int get hashCode => 0;
185 205
186 String toString() => '*'; 206 String toString() => '*';
187 } 207 }
188 208
189 /// A node matching zero or more characters that may be separators. 209 /// A node matching zero or more characters that may be separators.
190 class DoubleStarNode extends AstNode { 210 class DoubleStarNode extends AstNode {
191 /// The path context for the glob. 211 /// The path context for the glob.
192 /// 212 ///
193 /// This is used to determine what absolute paths look like. 213 /// This is used to determine what absolute paths look like.
194 final p.Context _context; 214 final p.Context _context;
195 215
196 DoubleStarNode(this._context); 216 DoubleStarNode(this._context, {bool caseSensitive: true})
217 : super._(caseSensitive);
197 218
198 String _toRegExp() { 219 String _toRegExp() {
199 // Double star shouldn't match paths with a leading "../", since these paths 220 // Double star shouldn't match paths with a leading "../", since these paths
200 // wouldn't be listed with this glob. We only check for "../" at the 221 // wouldn't be listed with this glob. We only check for "../" at the
201 // beginning since the paths are normalized before being checked against the 222 // beginning since the paths are normalized before being checked against the
202 // glob. 223 // glob.
203 var buffer = new StringBuffer()..write(r'(?!^(?:\.\./|'); 224 var buffer = new StringBuffer()..write(r'(?!^(?:\.\./|');
204 225
205 // A double star at the beginning of the glob also shouldn't match absolute 226 // A double star at the beginning of the glob also shouldn't match absolute
206 // paths, since those also wouldn't be listed. Which root patterns we look 227 // paths, since those also wouldn't be listed. Which root patterns we look
(...skipping 15 matching lines...) Expand all
222 243
223 bool operator==(Object other) => other is DoubleStarNode; 244 bool operator==(Object other) => other is DoubleStarNode;
224 245
225 int get hashCode => 1; 246 int get hashCode => 1;
226 247
227 String toString() => '**'; 248 String toString() => '**';
228 } 249 }
229 250
230 /// A node matching a single non-separator character. 251 /// A node matching a single non-separator character.
231 class AnyCharNode extends AstNode { 252 class AnyCharNode extends AstNode {
232 AnyCharNode(); 253 AnyCharNode({bool caseSensitive: true}) : super._(caseSensitive);
233 254
234 String _toRegExp() => '[^/]'; 255 String _toRegExp() => '[^/]';
235 256
236 bool operator==(Object other) => other is AnyCharNode; 257 bool operator==(Object other) => other is AnyCharNode;
237 258
238 int get hashCode => 2; 259 int get hashCode => 2;
239 260
240 String toString() => '?'; 261 String toString() => '?';
241 } 262 }
242 263
243 /// A node matching a single character in a range of options. 264 /// A node matching a single character in a range of options.
244 class RangeNode extends AstNode { 265 class RangeNode extends AstNode {
245 /// The ranges matched by this node. 266 /// The ranges matched by this node.
246 /// 267 ///
247 /// The ends of the ranges are unicode code points. 268 /// The ends of the ranges are unicode code points.
248 final Set<Range> ranges; 269 final Set<Range> ranges;
249 270
250 /// Whether this range was negated. 271 /// Whether this range was negated.
251 final bool negated; 272 final bool negated;
252 273
253 RangeNode(Iterable<Range> ranges, {this.negated}) 274 RangeNode(Iterable<Range> ranges, {this.negated, bool caseSensitive: true})
254 : ranges = ranges.toSet(); 275 : ranges = ranges.toSet(),
276 super._(caseSensitive);
255 277
256 OptionsNode flattenOptions() { 278 OptionsNode flattenOptions() {
257 if (negated || ranges.any((range) => !range.isSingleton)) { 279 if (negated || ranges.any((range) => !range.isSingleton)) {
258 return super.flattenOptions(); 280 return super.flattenOptions();
259 } 281 }
260 282
261 // If a range explicitly lists a set of characters, return each character as 283 // If a range explicitly lists a set of characters, return each character as
262 // a separate expansion. 284 // a separate expansion.
263 return new OptionsNode(ranges.map((range) { 285 return new OptionsNode(ranges.map((range) {
264 return new SequenceNode([ 286 return new SequenceNode([
265 new LiteralNode(new String.fromCharCodes([range.min])) 287 new LiteralNode(new String.fromCharCodes([range.min]),
266 ]); 288 caseSensitive: caseSensitive)
267 })); 289 ], caseSensitive: caseSensitive);
290 }), caseSensitive: caseSensitive);
268 } 291 }
269 292
270 String _toRegExp() { 293 String _toRegExp() {
271 var buffer = new StringBuffer(); 294 var buffer = new StringBuffer();
272 295
273 var containsSeparator = ranges.any((range) => range.contains(_SEPARATOR)); 296 var containsSeparator = ranges.any((range) => range.contains(_SEPARATOR));
274 if (!negated && containsSeparator) { 297 if (!negated && containsSeparator) {
275 // Add `(?!/)` because ranges are never allowed to match separators. 298 // Add `(?!/)` because ranges are never allowed to match separators.
276 buffer.write('(?!/)'); 299 buffer.write('(?!/)');
277 } 300 }
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
318 } 341 }
319 342
320 /// A node that matches one of several options. 343 /// A node that matches one of several options.
321 class OptionsNode extends AstNode { 344 class OptionsNode extends AstNode {
322 /// The options to match. 345 /// The options to match.
323 final List<SequenceNode> options; 346 final List<SequenceNode> options;
324 347
325 bool get canMatchAbsolute => options.any((node) => node.canMatchAbsolute); 348 bool get canMatchAbsolute => options.any((node) => node.canMatchAbsolute);
326 bool get canMatchRelative => options.any((node) => node.canMatchRelative); 349 bool get canMatchRelative => options.any((node) => node.canMatchRelative);
327 350
328 OptionsNode(Iterable<SequenceNode> options) 351 OptionsNode(Iterable<SequenceNode> options, {bool caseSensitive: true})
329 : options = options.toList(); 352 : options = options.toList(),
353 super._(caseSensitive);
330 354
331 OptionsNode flattenOptions() => new OptionsNode( 355 OptionsNode flattenOptions() => new OptionsNode(
332 options.expand((option) => option.flattenOptions().options)); 356 options.expand((option) => option.flattenOptions().options),
357 caseSensitive: caseSensitive);
333 358
334 String _toRegExp() => 359 String _toRegExp() =>
335 '(?:${options.map((option) => option._toRegExp()).join("|")})'; 360 '(?:${options.map((option) => option._toRegExp()).join("|")})';
336 361
337 bool operator==(Object other) => other is OptionsNode && 362 bool operator==(Object other) => other is OptionsNode &&
338 const UnorderedIterableEquality().equals(options, other.options); 363 const UnorderedIterableEquality().equals(options, other.options);
339 364
340 int get hashCode => const UnorderedIterableEquality().hash(options); 365 int get hashCode => const UnorderedIterableEquality().hash(options);
341 366
342 String toString() => '{${options.join(',')}}'; 367 String toString() => '{${options.join(',')}}';
(...skipping 10 matching lines...) Expand all
353 final p.Context _context; 378 final p.Context _context;
354 379
355 bool get canMatchAbsolute { 380 bool get canMatchAbsolute {
356 var nativeText = _context.style == p.Style.windows ? 381 var nativeText = _context.style == p.Style.windows ?
357 text.replaceAll('/', '\\') : text; 382 text.replaceAll('/', '\\') : text;
358 return _context.isAbsolute(nativeText); 383 return _context.isAbsolute(nativeText);
359 } 384 }
360 385
361 bool get canMatchRelative => !canMatchAbsolute; 386 bool get canMatchRelative => !canMatchAbsolute;
362 387
363 LiteralNode(this.text, [this._context]); 388 LiteralNode(this.text, {p.Context context, bool caseSensitive: true})
389 : _context = context,
390 super._(caseSensitive);
364 391
365 String _toRegExp() => regExpQuote(text); 392 String _toRegExp() => regExpQuote(text);
366 393
367 bool operator==(Object other) => other is LiteralNode && other.text == text; 394 bool operator==(Object other) => other is LiteralNode && other.text == text;
368 395
369 int get hashCode => text.hashCode; 396 int get hashCode => text.hashCode;
370 397
371 String toString() => text; 398 String toString() => text;
372 } 399 }
OLDNEW
« no previous file with comments | « packages/glob/lib/glob.dart ('k') | packages/glob/lib/src/list_tree.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698