| Index: compiler/java/com/google/dart/compiler/parser/DartParser.java
|
| diff --git a/compiler/java/com/google/dart/compiler/parser/DartParser.java b/compiler/java/com/google/dart/compiler/parser/DartParser.java
|
| index ccd91a316966be5b6406f33429cd10052124e19a..a840361747924ee7b1876a7779df48c423efbe1f 100644
|
| --- a/compiler/java/com/google/dart/compiler/parser/DartParser.java
|
| +++ b/compiler/java/com/google/dart/compiler/parser/DartParser.java
|
| @@ -1214,7 +1214,7 @@ public class DartParser extends CompletionHooksParserBase {
|
| reportError(position(), ParserErrorCode.NATIVE_ONLY_CORE_LIB);
|
| }
|
| if (match(Token.STRING)) {
|
| - parseString();
|
| + parseStringWithPasting();
|
| }
|
| if (match(Token.LBRACE) || match(Token.ARROW)) {
|
| // dom_frog.dart has non-static native methods with string and block
|
| @@ -1838,10 +1838,96 @@ public class DartParser extends CompletionHooksParserBase {
|
| return done(new DartConditional(result, yes, no));
|
| }
|
|
|
| + private boolean looksLikeStringInterpolation() {
|
| + int peekAhead = 0;
|
| + while (true) {
|
| + switch (peek(peekAhead++)) {
|
| + case STRING:
|
| + break;
|
| + case STRING_SEGMENT:
|
| + case STRING_LAST_SEGMENT:
|
| + case STRING_EMBED_EXP_START:
|
| + case STRING_EMBED_EXP_END:
|
| + return true;
|
| + default:
|
| + return false;
|
| + }
|
| + }
|
| + }
|
| + /**
|
| + * Pastes together adjacent strings. Re-uses the StringInterpolation
|
| + * node if there is more than one ajacent string.
|
| + */
|
| + private DartExpression parseStringWithPasting() {
|
| + List<DartExpression> expressions = new ArrayList<DartExpression>();
|
| + if (looksLikeStringInterpolation()) {
|
| + beginStringInterpolation();
|
| + } else {
|
| + beginLiteral();
|
| + }
|
| + DartExpression result = null;
|
| + boolean foundStringInterpolation = false;
|
| + do {
|
| + result = null;
|
| + switch(peek(0)) {
|
| + case STRING:
|
| + case STRING_SEGMENT:
|
| + case STRING_EMBED_EXP_START:
|
| + // another string is coming, glue it together.
|
| + result = parseString();
|
| + if (result != null) {
|
| + expressions.add(result);
|
| + }
|
| + if (result instanceof DartStringInterpolation) {
|
| + foundStringInterpolation = true;
|
| + }
|
| + break;
|
| + }
|
| + } while (result != null);
|
| +
|
| + if (expressions.size() == 0) {
|
| + return doneWithoutConsuming(null);
|
| + } else if (expressions.size() == 1) {
|
| + return done(expressions.get(0));
|
| + }
|
| +
|
| + if (foundStringInterpolation) {
|
| + DartStringInterpolationBuilder builder = new DartStringInterpolationBuilder();
|
| + // Create a new DartStringInterpolation object from the expressions.
|
| + boolean first = true;
|
| + for (DartExpression expr : expressions) {
|
| + if (!first) {
|
| + // pad between interpolations with a dummy expression
|
| + builder.addExpression(DartStringLiteral.get(""));
|
| + }
|
| + if (expr instanceof DartStringInterpolation) {
|
| + builder.addInterpolation((DartStringInterpolation)expr);
|
| + } else if (expr instanceof DartStringLiteral) {
|
| + builder.addString((DartStringLiteral)expr);
|
| + } else {
|
| + throw new InternalCompilerException("Expected String or StringInterpolation");
|
| + }
|
| + first = false;
|
| + }
|
| + return done(builder.buildInterpolation());
|
| + }
|
| +
|
| + // Synthesize a single String literal
|
| + StringBuilder builder = new StringBuilder();
|
| + for (DartExpression expr : expressions) {
|
| + builder.append(((DartStringLiteral)expr).getValue());
|
| + }
|
| + return done(DartStringLiteral.get(builder.toString()));
|
| + }
|
| +
|
| private DartExpression parseString() {
|
| switch(peek(0)) {
|
| - case STRING:
|
| - return parseLiteral();
|
| + case STRING: {
|
| + beginLiteral();
|
| + consume(Token.STRING);
|
| + return done(DartStringLiteral.get(ctx.getTokenString()));
|
| + }
|
| +
|
| case STRING_SEGMENT:
|
| case STRING_EMBED_EXP_START:
|
| return parseStringInterpolation();
|
| @@ -1909,11 +1995,6 @@ public class DartParser extends CompletionHooksParserBase {
|
| return done(DartIntegerLiteral.get(new BigInteger(number, 16)));
|
| }
|
|
|
| - case STRING: {
|
| - consume(Token.STRING);
|
| - return done(DartStringLiteral.get(ctx.getTokenString()));
|
| - }
|
| -
|
| case LBRACE: {
|
| return done(parseMapLiteral(false, null));
|
| }
|
| @@ -1966,10 +2047,38 @@ public class DartParser extends CompletionHooksParserBase {
|
| }
|
|
|
| /**
|
| - * <pre>
|
| - * mapLiteral
|
| - * : '{' (mapLiteralEntry (',' mapLiteralEntry)* ','?)? '}'
|
| + * mapLiteralEntry
|
| + * : STRING ':' expression
|
| * ;
|
| + */
|
| + private DartMapLiteralEntry parseMapLiteralEntry() {
|
| + beginMapLiteralEntry();
|
| + // Parse the key.
|
| + DartExpression keyExpr = parseStringWithPasting();
|
| + if (keyExpr == null) {
|
| + return done(null);
|
| + }
|
| + // Parse the value.
|
| + DartExpression value;
|
| + if (expect(Token.COLON)) {
|
| + value = parseExpression();
|
| + } else {
|
| + value = doneWithoutConsuming(DartNullLiteral.get());
|
| + }
|
| + return done(new DartMapLiteralEntry(keyExpr, value));
|
| + }
|
| + private boolean looksLikeString() {
|
| + switch(peek(0)) {
|
| + case STRING:
|
| + case STRING_SEGMENT:
|
| + case STRING_EMBED_EXP_START:
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| + * <pre> mapLiteral : '{' (mapLiteralEntry (',' mapLiteralEntry)* ','?)? '}' ;
|
| * </pre>
|
| */
|
| private DartExpression parseMapLiteral(boolean isConst, List<DartTypeNode> typeArguments) {
|
| @@ -1979,11 +2088,22 @@ public class DartParser extends CompletionHooksParserBase {
|
| List<DartMapLiteralEntry> entries = new ArrayList<DartMapLiteralEntry>();
|
|
|
| while (!match(Token.RBRACE) && !match(Token.EOS)) {
|
| + if (!looksLikeString()) {
|
| + ctx.advance();
|
| + reportError(position(), ParserErrorCode.EXPECTED_STRING_LITERAL_MAP_ENTRY_KEY);
|
| + if (peek(0) == Token.COMMA) {
|
| + // a common error is to put an empty entry in the list, allow it to
|
| + // recover.
|
| + continue;
|
| + } else {
|
| + break;
|
| + }
|
| + }
|
| DartMapLiteralEntry entry = parseMapLiteralEntry();
|
| if (entry != null) {
|
| entries.add(entry);
|
| }
|
| - switch(peek(0)) {
|
| + switch (peek(0)) {
|
| case COMMA:
|
| consume(Token.COMMA);
|
| break;
|
| @@ -2005,28 +2125,6 @@ public class DartParser extends CompletionHooksParserBase {
|
| }
|
|
|
| /**
|
| - * mapLiteralEntry
|
| - * : STRING ':' expression
|
| - * ;
|
| - */
|
| - private DartMapLiteralEntry parseMapLiteralEntry() {
|
| - beginMapLiteralEntry();
|
| - // Parse the key.
|
| - DartExpression keyExpr = parseString();
|
| - if (keyExpr == null) {
|
| - return done(null);
|
| - }
|
| - // Parse the value.
|
| - DartExpression value;
|
| - if (expect(Token.COLON)) {
|
| - value = parseExpression();
|
| - } else {
|
| - value = doneWithoutConsuming(DartNullLiteral.get());
|
| - }
|
| - return done(new DartMapLiteralEntry(keyExpr, value));
|
| - }
|
| -
|
| - /**
|
| * // The array literal syntax doesn't allow elided elements, unlike
|
| * // in ECMAScript.
|
| *
|
| @@ -2150,6 +2248,12 @@ public class DartParser extends CompletionHooksParserBase {
|
| lastSeen = LastSeenNode.EXPRESSION;
|
| }
|
|
|
| + void addInterpolation(DartStringInterpolation interpolation) {
|
| + strings.addAll(interpolation.getStrings());
|
| + expressions.addAll(interpolation.getExpressions());
|
| + lastSeen = LastSeenNode.STRING;
|
| + }
|
| +
|
| DartStringInterpolation buildInterpolation() {
|
| if (strings.size() == expressions.size()) {
|
| strings.add(DartStringLiteral.get(""));
|
| @@ -2507,12 +2611,15 @@ public class DartParser extends CompletionHooksParserBase {
|
| return done(literal);
|
| }
|
|
|
| + case STRING:
|
| case STRING_SEGMENT:
|
| - case STRING_LAST_SEGMENT:
|
| case STRING_EMBED_EXP_START: {
|
| - return parseStringInterpolation();
|
| + return parseStringWithPasting();
|
| }
|
|
|
| + case STRING_LAST_SEGMENT:
|
| + throw new InternalCompilerException("Invariant Broken");
|
| +
|
| default: {
|
| return parseLiteral();
|
| }
|
| @@ -3073,7 +3180,7 @@ public class DartParser extends CompletionHooksParserBase {
|
|
|
| /**
|
| * Parse a function declaration.
|
| - *
|
| + *
|
| * <pre>
|
| * nonLabelledStatement : ...
|
| * | functionDeclaration functionBody
|
| @@ -3086,7 +3193,7 @@ public class DartParser extends CompletionHooksParserBase {
|
| * | returnType? identifier formalParameterList
|
| * ;
|
| * </pre>
|
| - *
|
| + *
|
| * @return a {@link DartStatement} representing the function declaration or <code>null</code> if
|
| * code ends with function invocation, so this is not function declaration.
|
| */
|
|
|