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

Side by Side Diff: pkg/status_file/lib/status_file.dart

Issue 2984203002: Move the status file parser into its own package. (Closed)
Patch Set: 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 | « pkg/status_file/lib/src/expression.dart ('k') | pkg/status_file/pubspec.yaml » ('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) 2017, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2017, 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 import 'dart:io'; 5 import 'dart:io';
6 6
7 import 'package:path/path.dart' as p;
8
7 import 'environment.dart'; 9 import 'environment.dart';
8 import 'expectation.dart'; 10 import 'expectation.dart';
9 import 'path.dart'; 11 import 'src/expression.dart';
10 import 'status_expression.dart';
11 12
12 /// Matches the header that begins a new section, like: 13 /// Matches the header that begins a new section, like:
13 /// 14 ///
14 /// [ $compiler == dart2js && $minified ] 15 /// [ $compiler == dart2js && $minified ]
15 final _sectionPattern = new RegExp(r"^\[(.+?)\]"); 16 final _sectionPattern = new RegExp(r"^\[(.+?)\]");
16 17
17 /// Matches an entry that defines the status for a path in the current section, 18 /// Matches an entry that defines the status for a path in the current section,
18 /// like: 19 /// like:
19 /// 20 ///
20 /// some/path/to/some_test: Pass || Fail 21 /// some/path/to/some_test: Pass || Fail
(...skipping 18 matching lines...) Expand all
39 /// comma-separated list of [Expectation]s. The path may point to an individual 40 /// comma-separated list of [Expectation]s. The path may point to an individual
40 /// file, or a directory, in which case it applies to all files under that path. 41 /// file, or a directory, in which case it applies to all files under that path.
41 /// 42 ///
42 /// Entries may also appear before any section header, in which case they 43 /// Entries may also appear before any section header, in which case they
43 /// always apply. 44 /// always apply.
44 class StatusFile { 45 class StatusFile {
45 final String _path; 46 final String _path;
46 final List<StatusSection> sections = []; 47 final List<StatusSection> sections = [];
47 48
48 /// Parses the status file at [_path]. 49 /// Parses the status file at [_path].
50 ///
51 /// Throws a [SyntaxError] if the file could not be parsed.
49 StatusFile.read(this._path) { 52 StatusFile.read(this._path) {
50 var lines = new File(_path).readAsLinesSync(); 53 var lines = new File(_path).readAsLinesSync();
51 54
52 // The current section whose rules are being parsed. 55 // The current section whose rules are being parsed.
53 StatusSection section; 56 StatusSection section;
54 57
55 var lineNumber = 0; 58 var lineNumber = 0;
56 59
57 for (var line in lines) { 60 for (var line in lines) {
58 lineNumber++; 61 lineNumber++;
59 62
60 fail(String message, [List<String> errors]) { 63 fail(String message, [List<String> errors]) {
61 print('$message in "$_shortPath" line $lineNumber:\n$line'); 64 throw new SyntaxError(_shortPath, lineNumber, line, message, errors);
62
63 if (errors != null) {
64 for (var error in errors) {
65 print("- ${error.replaceAll('\n', '\n ')}");
66 }
67 }
68 exit(1);
69 } 65 }
70 66
71 // Strip off the comment and whitespace. 67 // Strip off the comment and whitespace.
72 var source = line; 68 var source = line;
73 var comment = ""; 69 var comment = "";
74 var hashIndex = line.indexOf('#'); 70 var hashIndex = line.indexOf('#');
75 if (hashIndex >= 0) { 71 if (hashIndex >= 0) {
76 source = line.substring(0, hashIndex); 72 source = line.substring(0, hashIndex);
77 comment = line.substring(hashIndex); 73 comment = line.substring(hashIndex);
78 } 74 }
79 source = source.trim(); 75 source = source.trim();
80 76
81 // Ignore empty (or comment-only) lines. 77 // Ignore empty (or comment-only) lines.
82 if (source.isEmpty) continue; 78 if (source.isEmpty) continue;
83 79
84 // See if we are starting a new section. 80 // See if we are starting a new section.
85 var match = _sectionPattern.firstMatch(source); 81 var match = _sectionPattern.firstMatch(source);
86 if (match != null) { 82 if (match != null) {
87 try { 83 try {
88 var condition = Expression.parse(match[1].trim()); 84 section = new StatusSection(Expression.parse(match[1].trim()));
89
90 var errors = <String>[];
91 condition.validate(errors);
92
93 if (errors.isNotEmpty) {
94 var s = errors.length > 1 ? "s" : "";
95 fail('Validation error$s', errors);
96 }
97
98 section = new StatusSection(condition);
99 sections.add(section); 85 sections.add(section);
100 } on FormatException { 86 } on FormatException {
101 fail("Status expression syntax error"); 87 fail("Status expression syntax error");
102 } 88 }
103 continue; 89 continue;
104 } 90 }
105 91
106 // Otherwise, it should be a new entry under the current section. 92 // Otherwise, it should be a new entry under the current section.
107 match = _entryPattern.firstMatch(source); 93 match = _entryPattern.firstMatch(source);
108 if (match != null) { 94 if (match != null) {
(...skipping 19 matching lines...) Expand all
128 } 114 }
129 115
130 section.entries.add(new StatusEntry(path, expectations, issue)); 116 section.entries.add(new StatusEntry(path, expectations, issue));
131 continue; 117 continue;
132 } 118 }
133 119
134 fail("Unrecognized input"); 120 fail("Unrecognized input");
135 } 121 }
136 } 122 }
137 123
124 /// Validates that the variables and values used in all of the section
125 /// condition expressions are defined in [environment].
126 ///
127 /// Throws a [SyntaxError] on the first found error.
128 void validate(Environment environment) {
129 // TODO(rnystrom): It would be more useful if it reported all of the errors
130 // instead of stopping on the first.
131 for (var section in sections) {
132 if (section._condition == null) continue;
133
134 var errors = <String>[];
135 section._condition.validate(environment, errors);
136
137 if (errors.isNotEmpty) {
138 var s = errors.length > 1 ? "s" : "";
139 throw new SyntaxError(_shortPath, section.lineNumber,
140 "[ ${section._condition} ]", 'Validation error$s', errors);
141 }
142 }
143 }
144
138 /// Gets the path to this status file relative to the Dart repo root. 145 /// Gets the path to this status file relative to the Dart repo root.
139 String get _shortPath { 146 String get _shortPath {
140 var repoRoot = new Path(Platform.script 147 var repoRoot = p.join(p.dirname(p.fromUri(Platform.script)), "../../../");
141 .toFilePath(windows: Platform.operatingSystem == "windows")) 148 return p.normalize(p.relative(_path, from: repoRoot));
142 .join(new Path("../../../../"));
143 return new Path(_path).relativeTo(repoRoot).toString();
144 } 149 }
145 150
146 /// Returns the issue number embedded in [comment] or `null` if there is none. 151 /// Returns the issue number embedded in [comment] or `null` if there is none.
147 int _issueNumber(String comment) { 152 int _issueNumber(String comment) {
148 var match = _issuePattern.firstMatch(comment); 153 var match = _issuePattern.firstMatch(comment);
149 if (match == null) return null; 154 if (match == null) return null;
150 155
151 return int.parse(match[1]); 156 return int.parse(match[1]);
152 } 157 }
153 158
154 String toString() { 159 String toString() {
155 var buffer = new StringBuffer(); 160 var buffer = new StringBuffer();
156 for (var section in sections) { 161 for (var section in sections) {
157 buffer.writeln("[${section._condition}]"); 162 buffer.writeln("[ ${section._condition} ]");
158 163
159 for (var entry in section.entries) { 164 for (var entry in section.entries) {
160 buffer.write("${entry.path}: ${entry.expectations.join(', ')}"); 165 buffer.write("${entry.path}: ${entry.expectations.join(', ')}");
161 if (entry.issue != null) buffer.write(" # Issue ${entry.issue}"); 166 if (entry.issue != null) buffer.write(" # Issue ${entry.issue}");
162 buffer.writeln(); 167 buffer.writeln();
163 } 168 }
164 169
165 buffer.writeln(); 170 buffer.writeln();
166 } 171 }
167 172
(...skipping 21 matching lines...) Expand all
189 } 194 }
190 195
191 /// Describes the test status of the file or files at a given path. 196 /// Describes the test status of the file or files at a given path.
192 class StatusEntry { 197 class StatusEntry {
193 final String path; 198 final String path;
194 final List<Expectation> expectations; 199 final List<Expectation> expectations;
195 final int issue; 200 final int issue;
196 201
197 StatusEntry(this.path, this.expectations, this.issue); 202 StatusEntry(this.path, this.expectations, this.issue);
198 } 203 }
204
205 /// Error thrown when a parse or validation error occurs in a [StatusFile].
206 class SyntaxError implements Exception {
207 final String file;
208 final int lineNumber;
209 final String line;
210 final String message;
211 final List<String> errors;
212
213 SyntaxError(this.file, this.lineNumber, this.line, this.message, this.errors);
214
215 String toString() {
216 var buffer = new StringBuffer();
217 buffer.writeln('$message in "$file" line $lineNumber:');
218 buffer.writeln(line);
219
220 if (errors != null) {
221 for (var error in errors) {
222 buffer.writeln("- ${error.replaceAll('\n', '\n ')}");
223 }
224 }
225
226 return buffer.toString().trimRight();
227 }
228 }
OLDNEW
« no previous file with comments | « pkg/status_file/lib/src/expression.dart ('k') | pkg/status_file/pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698