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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/glob/lib/glob.dart ('k') | pkg/glob/lib/src/parser.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library glob.ast;
6
7 import 'package:path/path.dart' as p;
8
9 import 'utils.dart';
10
11 const _SEPARATOR = 0x2F; // "/"
12
13 /// A node in the abstract syntax tree for a glob.
14 abstract class AstNode {
15 /// The cached regular expression that this AST was compiled into.
16 RegExp _regExp;
17
18 /// Whether this glob could match an absolute path.
19 ///
20 /// Either this or [canMatchRelative] or both will be true.
21 final bool canMatchAbsolute = false;
22
23 /// Whether this glob could match a relative path.
24 ///
25 /// Either this or [canMatchRelative] or both will be true.
26 final bool canMatchRelative = true;
27
28 /// Returns whether this glob matches [string].
29 bool matches(String string) {
30 if (_regExp == null) _regExp = new RegExp('^${_toRegExp()}\$');
31 return _regExp.hasMatch(string);
32 }
33
34 /// Subclasses should override this to return a regular expression component.
35 String _toRegExp();
36 }
37
38 /// A sequence of adjacent AST nodes.
39 class SequenceNode extends AstNode {
40 /// The nodes in the sequence.
41 final List<AstNode> nodes;
42
43 bool get canMatchAbsolute => nodes.first.canMatchAbsolute;
44 bool get canMatchRelative => nodes.first.canMatchRelative;
45
46 SequenceNode(Iterable<AstNode> nodes)
47 : nodes = nodes.toList();
48
49 String _toRegExp() => nodes.map((node) => node._toRegExp()).join();
50
51 String toString() => nodes.join();
52 }
53
54 /// A node matching zero or more non-separator characters.
55 class StarNode extends AstNode {
56 StarNode();
57
58 String _toRegExp() => '[^/]*';
59
60 String toString() => '*';
61 }
62
63 /// A node matching zero or more characters that may be separators.
64 class DoubleStarNode extends AstNode {
65 /// The path context for the glob.
66 ///
67 /// This is used to determine what absolute paths look like.
68 final p.Context _context;
69
70 DoubleStarNode(this._context);
71
72 String _toRegExp() {
73 // Double star shouldn't match paths with a leading "../", since these paths
74 // wouldn't be listed with this glob. We only check for "../" at the
75 // beginning since the paths are normalized before being checked against the
76 // glob.
77 var buffer = new StringBuffer()..write(r'(?!^(?:\.\./|');
78
79 // A double star at the beginning of the glob also shouldn't match absolute
80 // paths, since those also wouldn't be listed. Which root patterns we look
81 // for depends on the style of path we're matching.
82 if (_context.style == p.Style.posix) {
83 buffer.write(r'/');
84 } else if (_context.style == p.Style.windows) {
85 buffer.write(r'//|[A-Za-z]:/');
86 } else {
87 assert(_context.style == p.Style.url);
88 buffer.write(r'[a-zA-Z][-+.a-zA-Z\d]*://|/');
89 }
90
91 // Use `[^]` rather than `.` so that it matches newlines as well.
92 buffer.write(r'))[^]*');
93
94 return buffer.toString();
95 }
96
97 String toString() => '**';
98 }
99
100 /// A node matching a single non-separator character.
101 class AnyCharNode extends AstNode {
102 AnyCharNode();
103
104 String _toRegExp() => '[^/]';
105
106 String toString() => '?';
107 }
108
109 /// A node matching a single character in a range of options.
110 class RangeNode extends AstNode {
111 /// The ranges matched by this node.
112 ///
113 /// The ends of the ranges are unicode code points.
114 final Set<Range> ranges;
115
116 /// Whether this range was negated.
117 final bool negated;
118
119 RangeNode(Iterable<Range> ranges, {this.negated})
120 : ranges = ranges.toSet();
121
122 String _toRegExp() {
123 var buffer = new StringBuffer();
124
125 var containsSeparator = ranges.any((range) => range.contains(_SEPARATOR));
126 if (!negated && containsSeparator) {
127 // Add `(?!/)` because ranges are never allowed to match separators.
128 buffer.write('(?!/)');
129 }
130
131 buffer.write('[');
132 if (negated) {
133 buffer.write('^');
134 // If the range doesn't itself exclude separators, exclude them ourselves,
135 // since ranges are never allowed to match them.
136 if (!containsSeparator) buffer.write('/');
137 }
138
139 for (var range in ranges) {
140 var start = new String.fromCharCodes([range.min]);
141 buffer.write(regExpQuote(start));
142 if (range.isSingleton) continue;
143 buffer.write('-');
144 buffer.write(regExpQuote(new String.fromCharCodes([range.max])));
145 }
146
147 buffer.write(']');
148 return buffer.toString();
149 }
150
151 String toString() {
152 var buffer = new StringBuffer()..write('[');
153 for (var range in ranges) {
154 buffer.writeCharCode(range.min);
155 if (range.isSingleton) continue;
156 buffer.write('-');
157 buffer.writeCharCode(range.max);
158 }
159 buffer.write(']');
160 return buffer.toString();
161 }
162 }
163
164 /// A node that matches one of several options.
165 class OptionsNode extends AstNode {
166 /// The options to match.
167 final List<SequenceNode> options;
168
169 bool get canMatchAbsolute => options.any((node) => node.canMatchAbsolute);
170 bool get canMatchRelative => options.any((node) => node.canMatchRelative);
171
172 OptionsNode(Iterable<SequenceNode> options)
173 : options = options.toList();
174
175 String _toRegExp() =>
176 '(?:${options.map((option) => option._toRegExp()).join("|")})';
177
178 String toString() => '{${options.join(',')}}';
179 }
180
181 /// A node that matches a literal string.
182 class LiteralNode extends AstNode {
183 /// The string to match.
184 final String text;
185
186 /// The path context for the glob.
187 ///
188 /// This is used to determine whether this could match an absolute path.
189 final p.Context _context;
190
191 bool get canMatchAbsolute {
192 var nativeText = _context.style == p.Style.windows ?
193 text.replaceAll('/', '\\') : text;
194 return _context.isAbsolute(nativeText);
195 }
196
197 bool get canMatchRelative => !canMatchAbsolute;
198
199 LiteralNode(this.text, this._context);
200
201 String _toRegExp() => regExpQuote(text);
202
203 String toString() => text;
204 }
OLDNEW
« 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