| 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 |