OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, 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('parser'); | |
6 | |
7 #import('dart:io'); | |
8 | |
9 #import('../../../lib/utf/utf.dart'); | |
10 | |
11 #import('../elements/elements.dart'); | |
12 #import('../scanner/scanner_implementation.dart'); | |
13 #import('../scanner/scannerlib.dart'); | |
14 #import('../tree/tree.dart'); | |
15 #import('../util/characters.dart'); | |
16 | |
17 #source('../diagnostic_listener.dart'); | |
18 #source('../../source.dart'); | |
19 #source('../scanner/byte_array_scanner.dart'); | |
20 #source('../scanner/byte_strings.dart'); | |
21 | |
22 int charCount = 0; | |
23 Stopwatch stopwatch; | |
24 | |
25 void main() { | |
26 filesWithCrashes = []; | |
27 stopwatch = new Stopwatch(); | |
28 MyOptions options = new MyOptions(); | |
29 | |
30 void printStats() { | |
31 int kb = (charCount / 1024).round().toInt(); | |
32 String stats = | |
33 '$classCount classes (${kb}Kb) in ${stopwatch.elapsedInMs()}ms'; | |
34 if (errorCount != 0) { | |
35 stats += ' with $errorCount errors'; | |
36 } | |
37 if (options.diet) { | |
38 print('Diet parsed $stats.'); | |
39 } else { | |
40 print('Parsed $stats.'); | |
41 } | |
42 if (filesWithCrashes.length !== 0) { | |
43 print('The following ${filesWithCrashes.length} files caused a crash:'); | |
44 for (String file in filesWithCrashes) { | |
45 print(file); | |
46 } | |
47 } | |
48 } | |
49 | |
50 for (String argument in new Options().arguments) { | |
51 if (argument == "--diet") { | |
52 options.diet = true; | |
53 continue; | |
54 } | |
55 if (argument == "--throw") { | |
56 options.throwOnError = true; | |
57 continue; | |
58 } | |
59 if (argument == "--scan-only") { | |
60 options.scanOnly = true; | |
61 continue; | |
62 } | |
63 if (argument == "--read-only") { | |
64 options.readOnly = true; | |
65 continue; | |
66 } | |
67 if (argument == "--ast") { | |
68 options.buildAst = true; | |
69 continue; | |
70 } | |
71 if (argument == "-") { | |
72 parseFilesFrom(stdin, options, printStats); | |
73 return; | |
74 } | |
75 stopwatch.start(); | |
76 parseFile(argument, options); | |
77 stopwatch.stop(); | |
78 } | |
79 | |
80 printStats(); | |
81 } | |
82 | |
83 void parseFile(String filename, MyOptions options) { | |
84 List<int> bytes = read(filename); | |
85 charCount += bytes.length; | |
86 if (options.readOnly) return; | |
87 MySourceFile file = new MySourceFile(filename, bytes); | |
88 final Listener listener = options.buildAst | |
89 ? new MyNodeListener(file, options) | |
90 : new MyListener(file); | |
91 final Parser parser = options.diet | |
92 ? new PartialParser(listener) | |
93 : new Parser(listener); | |
94 try { | |
95 Token token = scan(file); | |
96 if (!options.scanOnly) parser.parseUnit(token); | |
97 } catch (ParserError ex) { | |
98 if (options.throwOnError) { | |
99 throw; | |
100 } else { | |
101 print(ex); | |
102 } | |
103 } catch (MalformedInputException ex) { | |
104 // Already diagnosed. | |
105 } catch (var ex) { | |
106 print('Error in file: $filename'); | |
107 throw; | |
108 } | |
109 if (options.buildAst) { | |
110 MyNodeListener l = listener; | |
111 if (!l.nodes.isEmpty()) { | |
112 String message = 'Stack not empty after parsing'; | |
113 print(formatError(message, l.nodes.head.getBeginToken(), | |
114 l.nodes.head.getEndToken(), file)); | |
115 throw message; | |
116 } | |
117 } | |
118 } | |
119 | |
120 Token scan(MySourceFile source) { | |
121 Scanner scanner = new ByteArrayScanner(source.rawText); | |
122 try { | |
123 return scanner.tokenize(); | |
124 } catch (MalformedInputException ex) { | |
125 if (ex.position is Token) { | |
126 print(formatError(ex.message, ex.position, ex.position, source)); | |
127 } else { | |
128 Token fakeToken = new Token(QUESTION_INFO, ex.position); | |
129 print(formatError(ex.message, fakeToken, fakeToken, source)); | |
130 } | |
131 throw; | |
132 } | |
133 } | |
134 | |
135 var filesWithCrashes; | |
136 | |
137 void parseFilesFrom(InputStream input, MyOptions options, Function whenDone) { | |
138 void readLine(String line) { | |
139 stopwatch.start(); | |
140 try { | |
141 parseFile(line, options); | |
142 } catch (var ex, var trace) { | |
143 filesWithCrashes.add(line); | |
144 print(ex); | |
145 print(trace); | |
146 } | |
147 stopwatch.stop(); | |
148 } | |
149 forEachLine(input, readLine, whenDone); | |
150 } | |
151 | |
152 void forEachLine(InputStream input, | |
153 void lineHandler(String line), | |
154 void closeHandler()) { | |
155 StringInputStream stringStream = new StringInputStream(input); | |
156 stringStream.onLine = () { | |
157 String line; | |
158 while ((line = stringStream.readLine()) !== null) { | |
159 lineHandler(line); | |
160 } | |
161 }; | |
162 stringStream.onClosed = closeHandler; | |
163 } | |
164 | |
165 List<int> read(String filename) { | |
166 RandomAccessFile file = new File(filename).openSync(); | |
167 bool threw = true; | |
168 try { | |
169 int size = file.lengthSync(); | |
170 List<int> bytes = new ByteArray(size + 1); | |
171 file.readListSync(bytes, 0, size); | |
172 bytes[size] = $EOF; | |
173 threw = false; | |
174 return bytes; | |
175 } finally { | |
176 try { | |
177 file.closeSync(); | |
178 } catch (var ex) { | |
179 if (!threw) throw; | |
180 } | |
181 } | |
182 } | |
183 | |
184 int classCount = 0; | |
185 int errorCount = 0; | |
186 | |
187 class MyListener extends Listener { | |
188 final SourceFile file; | |
189 | |
190 MyListener(this.file); | |
191 | |
192 void beginClassDeclaration(Token token) { | |
193 classCount++; | |
194 } | |
195 | |
196 void beginInterface(Token token) { | |
197 classCount++; | |
198 } | |
199 | |
200 void error(String message, Token token) { | |
201 throw new ParserError(formatError(message, token, token, file)); | |
202 } | |
203 } | |
204 | |
205 String formatError(String message, Token beginToken, Token endToken, | |
206 SourceFile file) { | |
207 ++errorCount; | |
208 if (beginToken === null) return '${file.filename}: $message'; | |
209 String tokenString = endToken.toString(); | |
210 int begin = beginToken.charOffset; | |
211 int end = endToken.charOffset + tokenString.length; | |
212 return file.getLocationMessage(message, begin, end, true); | |
213 } | |
214 | |
215 class MyNodeListener extends NodeListener { | |
216 MyNodeListener(SourceFile file, MyOptions options) | |
217 : super(new MyCanceller(file, options), null); | |
218 | |
219 void beginClassDeclaration(Token token) { | |
220 classCount++; | |
221 } | |
222 | |
223 void beginInterface(Token token) { | |
224 classCount++; | |
225 } | |
226 | |
227 void endClassDeclaration(int interfacesCount, Token beginToken, | |
228 Token extendsKeyword, Token implementsKeyword, | |
229 Token endToken) { | |
230 super.endClassDeclaration(interfacesCount, beginToken, | |
231 extendsKeyword, implementsKeyword, | |
232 endToken); | |
233 ClassNode node = popNode(); // Discard ClassNode and assert the type. | |
234 } | |
235 | |
236 void endInterface(int supertypeCount, Token interfaceKeyword, | |
237 Token extendsKeyword, Token endToken) { | |
238 super.endInterface(supertypeCount, interfaceKeyword, extendsKeyword, | |
239 endToken); | |
240 ClassNode node = popNode(); // Discard ClassNode and assert the type. | |
241 } | |
242 | |
243 void endTopLevelFields(int count, Token beginToken, Token endToken) { | |
244 super.endTopLevelFields(count, beginToken, endToken); | |
245 VariableDefinitions node = popNode(); // Discard node and assert the type. | |
246 } | |
247 | |
248 void endFunctionTypeAlias(Token typedefKeyword, Token endToken) { | |
249 super.endFunctionTypeAlias(typedefKeyword, endToken); | |
250 Typedef node = popNode(); // Discard Typedef and assert type type. | |
251 } | |
252 | |
253 void endLibraryTag(bool hasPrefix, Token beginToken, Token endToken) { | |
254 super.endLibraryTag(hasPrefix, beginToken, endToken); | |
255 ScriptTag node = popNode(); // Discard ScriptTag and assert type type. | |
256 } | |
257 | |
258 void log(message) { | |
259 print(message); | |
260 } | |
261 } | |
262 | |
263 class MyCanceller implements DiagnosticListener { | |
264 final SourceFile file; | |
265 final MyOptions options; | |
266 | |
267 MyCanceller(this.file, this.options); | |
268 | |
269 void log(String message) {} | |
270 | |
271 void cancel([String reason, node, token, instruction, element]) { | |
272 Token beginToken; | |
273 Token endToken; | |
274 if (token !== null) { | |
275 beginToken = token; | |
276 endToken = token; | |
277 } else if (node !== null) { | |
278 beginToken = node.getBeginToken(); | |
279 endToken = node.getEndToken(); | |
280 } | |
281 String message = formatError(reason, beginToken, endToken, file); | |
282 if (options.throwOnError) throw new ParserError(message); | |
283 print(message); | |
284 } | |
285 } | |
286 | |
287 class MyOptions { | |
288 bool diet = false; | |
289 bool throwOnError = false; | |
290 bool scanOnly = false; | |
291 bool readOnly = false; | |
292 bool buildAst = false; | |
293 } | |
294 | |
295 class MySourceFile extends SourceFile { | |
296 final rawText; | |
297 var stringText; | |
298 | |
299 MySourceFile(filename, this.rawText) : super(filename, null); | |
300 | |
301 String get text() { | |
302 if (rawText is String) { | |
303 return rawText; | |
304 } else { | |
305 if (stringText === null) { | |
306 stringText = new String.fromCharCodes(rawText); | |
307 if (stringText.endsWith('\u0000')) { | |
308 // Strip trailing NUL used by ByteArrayScanner to signal EOF. | |
309 stringText = stringText.substring(0, stringText.length - 1); | |
310 } | |
311 } | |
312 return stringText; | |
313 } | |
314 } | |
315 | |
316 set text(String newText) { | |
317 throw "not supported"; | |
318 } | |
319 } | |
320 | |
321 // Hacks to allow sourcing in ../source.dart: | |
322 var world = const Mock(); | |
323 var options = const Mock(); | |
324 String _GREEN_COLOR = '\u001b[32m'; | |
325 String _RED_COLOR = '\u001b[31m'; | |
326 String _MAGENTA_COLOR = '\u001b[35m'; | |
327 String _NO_COLOR = '\u001b[0m'; | |
328 | |
329 class Mock { | |
330 const Mock(); | |
331 bool get useColors() => true; | |
332 internalError(message) { throw message.toString(); } | |
333 } | |
OLD | NEW |