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

Side by Side Diff: pkg/intl/lib/extract_messages.dart

Issue 140843002: [Intl] Cleanup some code (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 11 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
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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 /** 5 /**
6 * This is for use in extracting messages from a Dart program 6 * This is for use in extracting messages from a Dart program
7 * using the Intl.message() mechanism and writing them to a file for 7 * using the Intl.message() mechanism and writing them to a file for
8 * translation. This provides only the stub of a mechanism, because it 8 * translation. This provides only the stub of a mechanism, because it
9 * doesn't define how the file should be written. It provides an 9 * doesn't define how the file should be written. It provides an
10 * [IntlMessage] class that holds the extracted data and [parseString] 10 * [IntlMessage] class that holds the extracted data and [parseString]
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
105 * as that will be the nearest parent of the Intl.message invocation. 105 * as that will be the nearest parent of the Intl.message invocation.
106 */ 106 */
107 FormalParameterList parameters; 107 FormalParameterList parameters;
108 String name; 108 String name;
109 109
110 /** Return true if [node] matches the pattern we expect for Intl.message() */ 110 /** Return true if [node] matches the pattern we expect for Intl.message() */
111 bool looksLikeIntlMessage(MethodInvocation node) { 111 bool looksLikeIntlMessage(MethodInvocation node) {
112 const validNames = const ["message", "plural", "gender", "select"]; 112 const validNames = const ["message", "plural", "gender", "select"];
113 if (!validNames.contains(node.methodName.name)) return false; 113 if (!validNames.contains(node.methodName.name)) return false;
114 if (!(node.target is SimpleIdentifier)) return false; 114 if (!(node.target is SimpleIdentifier)) return false;
115 SimpleIdentifier target = node.target; 115 return !(node.target.token.toString() != "Intl");
Alan Knight 2014/01/28 01:33:46 Can we delete both "not"s and simplify this?
vicb 2014/01/28 07:52:45 I did hesitate and thought there might be tricky e
116 if (target.token.toString() != "Intl") return false;
117 return true;
118 } 116 }
119 117
120 Message _expectedInstance(String type) { 118 Message _expectedInstance(String type) {
121 switch (type) { 119 switch (type) {
122 case 'message' : return new MainMessage(); 120 case 'message' : return new MainMessage();
123 case 'plural' : return new Plural(); 121 case 'plural' : return new Plural();
124 case 'gender' : return new Gender(); 122 case 'gender' : return new Gender();
125 case 'select' : return new Select(); 123 case 'select' : return new Select();
126 default: return null; 124 default: return null;
127 } 125 }
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
168 } 166 }
169 167
170 /** 168 /**
171 * Examine method invocations to see if they look like calls to Intl.message. 169 * Examine method invocations to see if they look like calls to Intl.message.
172 * If we've found one, stop recursing. This is important because we can have 170 * If we've found one, stop recursing. This is important because we can have
173 * Intl.message(...Intl.plural...) and we don't want to treat the inner 171 * Intl.message(...Intl.plural...) and we don't want to treat the inner
174 * plural as if it was an outermost message. 172 * plural as if it was an outermost message.
175 */ 173 */
176 void visitMethodInvocation(MethodInvocation node) { 174 void visitMethodInvocation(MethodInvocation node) {
177 if (!addIntlMessage(node)) { 175 if (!addIntlMessage(node)) {
178 return super.visitMethodInvocation(node); 176 super.visitMethodInvocation(node);
179 } 177 }
180 } 178 }
181 179
182 /** 180 /**
183 * Check that the node looks like an Intl.message invocation, and create 181 * Check that the node looks like an Intl.message invocation, and create
184 * the [IntlMessage] object from it and store it in [messages]. Return true 182 * the [IntlMessage] object from it and store it in [messages]. Return true
185 * if we successfully extracted a message and should stop looking. Return 183 * if we successfully extracted a message and should stop looking. Return
186 * false if we didn't, so should continue recursing. 184 * false if we didn't, so should continue recursing.
187 */ 185 */
188 bool addIntlMessage(MethodInvocation node) { 186 bool addIntlMessage(MethodInvocation node) {
189 if (!looksLikeIntlMessage(node)) return false; 187 if (!looksLikeIntlMessage(node)) return false;
190 var reason = checkValidity(node); 188 var reason = checkValidity(node);
191 if (reason != null) { 189 if (reason != null) {
192 if (!suppressWarnings) { 190 if (!suppressWarnings) {
193 var err = new StringBuffer(); 191 var err = new StringBuffer()
194 err.write("Skipping invalid Intl.message invocation\n <$node>\n"); 192 ..write("Skipping invalid Intl.message invocation\n <$node>\n")
195 err.write(" reason: $reason\n"); 193 ..writeAll([" reason: $reason\n", _reportErrorLocation(node)]);
196 err.write(_reportErrorLocation(node));
197 warnings.add(err.toString()); 194 warnings.add(err.toString());
198 print(err); 195 print(err);
199 } 196 }
200 // We found one, but it's not valid. Stop recursing. 197 // We found one, but it's not valid. Stop recursing.
201 return true; 198 return true;
202 } 199 }
203 var message; 200 var message;
204 if (node.methodName.name == "message") { 201 if (node.methodName.name == "message") {
205 message = messageFromIntlMessageCall(node); 202 message = messageFromIntlMessageCall(node);
206 } else { 203 } else {
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
245 */ 242 */
246 MainMessage messageFromIntlMessageCall(MethodInvocation node) { 243 MainMessage messageFromIntlMessageCall(MethodInvocation node) {
247 244
248 void extractFromIntlCall(MainMessage message, List arguments) { 245 void extractFromIntlCall(MainMessage message, List arguments) {
249 try { 246 try {
250 var interpolation = new InterpolationVisitor(message); 247 var interpolation = new InterpolationVisitor(message);
251 arguments.first.accept(interpolation); 248 arguments.first.accept(interpolation);
252 message.messagePieces.addAll(interpolation.pieces); 249 message.messagePieces.addAll(interpolation.pieces);
253 } on IntlMessageExtractionException catch (e) { 250 } on IntlMessageExtractionException catch (e) {
254 message = null; 251 message = null;
255 var err = new StringBuffer(); 252 var err = new StringBuffer()
256 err.write("Error $e\n"); 253 ..writeAll(["Error ", e, "\nProcessing <", node, ">\n"])
257 err.write("Processing <$node>\n"); 254 ..write(_reportErrorLocation(node));
258 err.write(_reportErrorLocation(node));
259 print(err); 255 print(err);
260 warnings.add(err); 256 warnings.add(err);
261 } 257 }
262 } 258 }
263 259
264 void setValue(MainMessage message, String fieldName, Object fieldValue) { 260 void setValue(MainMessage message, String fieldName, Object fieldValue) {
265 message[fieldName] = fieldValue; 261 message[fieldName] = fieldValue;
266 } 262 }
267 263
268 return _messageFromNode(node, extractFromIntlCall, setValue); 264 return _messageFromNode(node, extractFromIntlCall, setValue);
(...skipping 28 matching lines...) Expand all
297 * Given an interpolation, find all of its chunks, validate that they are only 293 * Given an interpolation, find all of its chunks, validate that they are only
298 * simple variable substitutions or else Intl.plural/gender calls, 294 * simple variable substitutions or else Intl.plural/gender calls,
299 * and keep track of the pieces of text so that other parts 295 * and keep track of the pieces of text so that other parts
300 * of the program can deal with the simple string sections and the generated 296 * of the program can deal with the simple string sections and the generated
301 * parts separately. Note that this is a SimpleASTVisitor, so it only 297 * parts separately. Note that this is a SimpleASTVisitor, so it only
302 * traverses one level of children rather than automatically recursing. If we 298 * traverses one level of children rather than automatically recursing. If we
303 * find a plural or gender, which requires recursion, we do it with a separate 299 * find a plural or gender, which requires recursion, we do it with a separate
304 * special-purpose visitor. 300 * special-purpose visitor.
305 */ 301 */
306 class InterpolationVisitor extends SimpleASTVisitor { 302 class InterpolationVisitor extends SimpleASTVisitor {
307 Message message; 303 final Message message;
308 304
309 InterpolationVisitor(this.message); 305 InterpolationVisitor(this.message);
310 306
311 List pieces = []; 307 List pieces = [];
312 String get extractedMessage => pieces.join(); 308 String get extractedMessage => pieces.join();
313 309
314 void visitAdjacentStrings(AdjacentStrings node) { 310 void visitAdjacentStrings(AdjacentStrings node) {
315 node.visitChildren(this); 311 node.visitChildren(this);
316 super.visitAdjacentStrings(node); 312 super.visitAdjacentStrings(node);
317 } 313 }
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
366 /** 362 /**
367 * A visitor to extract information from Intl.plural/gender sends. Note that 363 * A visitor to extract information from Intl.plural/gender sends. Note that
368 * this is a SimpleASTVisitor, so it doesn't automatically recurse. So this 364 * this is a SimpleASTVisitor, so it doesn't automatically recurse. So this
369 * needs to be called where we expect a plural or gender immediately below. 365 * needs to be called where we expect a plural or gender immediately below.
370 */ 366 */
371 class PluralAndGenderVisitor extends SimpleASTVisitor { 367 class PluralAndGenderVisitor extends SimpleASTVisitor {
372 /** 368 /**
373 * A plural or gender always exists in the context of a parent message, 369 * A plural or gender always exists in the context of a parent message,
374 * which could in turn also be a plural or gender. 370 * which could in turn also be a plural or gender.
375 */ 371 */
376 ComplexMessage parent; 372 final ComplexMessage parent;
377 373
378 /** 374 /**
379 * The pieces of the message. We are given an initial version of this 375 * The pieces of the message. We are given an initial version of this
380 * from our parent and we add to it as we find additional information. 376 * from our parent and we add to it as we find additional information.
381 */ 377 */
382 List pieces; 378 List pieces;
383 379
384 PluralAndGenderVisitor(this.pieces, this.parent) : super() {}
385
386 /** This will be set to true if we find a plural or gender. */ 380 /** This will be set to true if we find a plural or gender. */
387 bool foundPluralOrGender = false; 381 bool foundPluralOrGender = false;
388 382
383 PluralAndGenderVisitor(this.pieces, this.parent) : super();
384
389 visitInterpolationExpression(InterpolationExpression node) { 385 visitInterpolationExpression(InterpolationExpression node) {
390 // TODO(alanknight): Provide better errors for malformed expressions. 386 // TODO(alanknight): Provide better errors for malformed expressions.
391 if (!looksLikePluralOrGender(node.expression)) return; 387 if (!looksLikePluralOrGender(node.expression)) return;
392 var reason = checkValidity(node.expression); 388 var reason = checkValidity(node.expression);
393 if (reason != null) throw reason; 389 if (reason != null) throw reason;
394 var message = messageFromMethodInvocation(node.expression); 390 var message = messageFromMethodInvocation(node.expression);
395 foundPluralOrGender = true; 391 foundPluralOrGender = true;
396 pieces.add(message); 392 pieces.add(message);
397 super.visitInterpolationExpression(node); 393 super.visitInterpolationExpression(node);
398 } 394 }
399 395
400 visitMethodInvocation(MethodInvocation node) { 396 visitMethodInvocation(MethodInvocation node) {
401 pieces.add(messageFromMethodInvocation(node)); 397 pieces.add(messageFromMethodInvocation(node));
402 super.visitMethodInvocation(node); 398 super.visitMethodInvocation(node);
403 } 399 }
404 400
405 /** Return true if [node] matches the pattern for plural or gender message.*/ 401 /** Return true if [node] matches the pattern for plural or gender message.*/
406 bool looksLikePluralOrGender(MethodInvocation node) { 402 bool looksLikePluralOrGender(MethodInvocation node) {
407 if (!["plural", "gender", "select"].contains(node.methodName.name)) { 403 if (!["plural", "gender", "select"].contains(node.methodName.name)) {
408 return false; 404 return false;
409 } 405 }
410 if (!(node.target is SimpleIdentifier)) return false; 406 if (!(node.target is SimpleIdentifier)) return false;
411 SimpleIdentifier target = node.target; 407 return !(node.target.token.toString() != "Intl");
Alan Knight 2014/01/28 01:33:46 Again, I think we can just remove both "not"s
vicb 2014/01/28 07:52:45 fixed
412 if (target.token.toString() != "Intl") return false;
413 return true;
414 } 408 }
415 409
416 /** 410 /**
417 * Returns a String describing why the node is invalid, or null if no 411 * Returns a String describing why the node is invalid, or null if no
418 * reason is found, so it's presumed valid. 412 * reason is found, so it's presumed valid.
419 */ 413 */
420 String checkValidity(MethodInvocation node) { 414 String checkValidity(MethodInvocation node) {
421 // TODO(alanknight): Add reasonable validity checks. 415 // TODO(alanknight): Add reasonable validity checks.
422 } 416 }
423 417
(...skipping 14 matching lines...) Expand all
438 message.parent = parent; 432 message.parent = parent;
439 433
440 var arguments = message.argumentsOfInterestFor(node); 434 var arguments = message.argumentsOfInterestFor(node);
441 arguments.forEach((key, value) { 435 arguments.forEach((key, value) {
442 try { 436 try {
443 var interpolation = new InterpolationVisitor(message); 437 var interpolation = new InterpolationVisitor(message);
444 value.accept(interpolation); 438 value.accept(interpolation);
445 message[key] = interpolation.pieces; 439 message[key] = interpolation.pieces;
446 } on IntlMessageExtractionException catch (e) { 440 } on IntlMessageExtractionException catch (e) {
447 message = null; 441 message = null;
448 var err = new StringBuffer(); 442 var err = new StringBuffer()
449 err.write("Error $e"); 443 ..writeAll(["Error ", $e, "\nProcessing <", node, ">"])
450 err.write("Processing <$node>"); 444 ..write(_reportErrorLocation(node));
451 err.write(_reportErrorLocation(node));
452 print(err); 445 print(err);
453 warnings.add(err); 446 warnings.add(err);
454 } 447 }
455 }); 448 });
456 var mainArg = node.argumentList.arguments.firstWhere( 449 var mainArg = node.argumentList.arguments.firstWhere(
457 (each) => each is! NamedExpression); 450 (each) => each is! NamedExpression);
458 if (mainArg is SimpleStringLiteral) { 451 if (mainArg is SimpleStringLiteral) {
459 message.mainArgument = mainArg.toString(); 452 message.mainArgument = mainArg.toString();
460 } else { 453 } else {
461 message.mainArgument = mainArg.name; 454 message.mainArgument = mainArg.name;
(...skipping 10 matching lines...) Expand all
472 * A message describing the error. 465 * A message describing the error.
473 */ 466 */
474 final String message; 467 final String message;
475 468
476 /** 469 /**
477 * Creates a new exception with an optional error [message]. 470 * Creates a new exception with an optional error [message].
478 */ 471 */
479 const IntlMessageExtractionException([this.message = ""]); 472 const IntlMessageExtractionException([this.message = ""]);
480 473
481 String toString() => "IntlMessageExtractionException: $message"; 474 String toString() => "IntlMessageExtractionException: $message";
482 } 475 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698