Search in sources :

Example 1 with SwitchBuilder

use of com.google.template.soy.jssrc.dsl.SwitchBuilder in project closure-templates by google.

the class AssistantForHtmlMsgs method generateMsgGroupCode.

/**
 * Returns a code chunk of idom instructions that output the contents of a translated message as
 * HTML. For example:
 *
 * <pre>
 *   {msg desc="Says hello to a person."}Hello {$name}!{/msg}
 * </pre>
 *
 * compiles to
 *
 * <pre>
 *   /** @desc Says hello to a person. *{@literal /}
 *   var MSG_EXTERNAL_6936162475751860807 = goog.getMsg(
 *       'Hello {$name}!',
 *       {'name': '\u00010\u0001'});
 *   var lastIndex_1153 = 0, partRe_1153 = /\x01\d+\x01/g, match_1153;
 *   do {
 *     match_1153 = partRe_1153.exec(MSG_EXTERNAL_6936162475751860807) || undefined;
 *     incrementalDom.text(goog.string.unescapeEntities(
 *         MSG_EXTERNAL_6936162475751860807.substring(
 *           lastIndex_1153, match_1153 && match_1153.index)));
 *     lastIndex_1153 = partRe_1153.lastIndex;
 *     switch (match_1153 && match_1153[0]) {
 *       case '\u00010\u0001':
 *         var dyn8 = opt_data.name;
 *         if (typeof dyn8 == 'function') dyn8();
 *         else if (dyn8 != null) incrementalDom.text(dyn8);
 *         break;
 *     }
 *   } while (match_1153);
 * </pre>
 *
 * Each interpolated MsgPlaceholderNode (either for HTML tags or for print statements) compiles to
 * a separate {@code case} statement.
 */
CodeChunk generateMsgGroupCode(MsgFallbackGroupNode node) {
    Preconditions.checkState(placeholderNames.isEmpty(), "This class is not reusable.");
    // Non-HTML {msg}s should be extracted into LetContentNodes and handled by jssrc.
    Preconditions.checkArgument(node.getHtmlContext() == HtmlContext.HTML_PCDATA, "AssistantForHtmlMsgs is only for HTML {msg}s.");
    // All of these helper variables must have uniquely-suffixed names because {msg}s can be nested.
    // It'd be nice to move this codegen to a Soy template...
    // The raw translated text, with placeholder placeholders.
    CodeChunk.WithValue translationVar = super.generateMsgGroupVariable(node);
    // If there are no placeholders, we don't need anything special (but we still need to unescape).
    if (placeholderNames.isEmpty()) {
        CodeChunk.WithValue unescape = GOOG_STRING_UNESCAPE_ENTITIES.call(translationVar);
        return INCREMENTAL_DOM_TEXT.call(unescape);
    }
    // it into a fresh variable.
    if (!translationVar.isCheap()) {
        translationVar = translationContext.codeGenerator().declarationBuilder().setRhs(translationVar).build().ref();
    }
    // Declare everything.
    // The mutable (tracking index of last match) regex to find the placeholder placeholders.
    VariableDeclaration regexVar = VariableDeclaration.builder("partRe_" + node.getId()).setRhs(CodeChunk.regexLiteral(PLACEHOLDER_REGEX)).build();
    // The current placeholder from the regex.
    VariableDeclaration matchVar = VariableDeclaration.builder("match_" + node.getId()).build();
    // The index of the end of the previous placeholder, where the next raw text run starts.
    VariableDeclaration lastIndexVar = VariableDeclaration.builder("lastIndex_" + node.getId()).setRhs(CodeChunk.number(0)).build();
    List<CodeChunk> doBody = new ArrayList<>();
    // match_XXX = partRe_XXX.exec(MSG_EXTERNAL_XXX) || undefined;
    doBody.add(matchVar.ref().assign(regexVar.ref().dotAccess("exec").call(translationVar).or(CodeChunk.id("undefined"), translationContext.codeGenerator())));
    // Replace null with undefined.  This is necessary to make substring() treat falsy as an omitted
    // parameter, so that it goes until the end of the string.  Otherwise, the non-numeric parameter
    // would be coerced to zero.
    // Emit the (possibly-empty) run of raw text since the last placeholder, until this placeholder,
    // or until the end of the source string.
    CodeChunk.WithValue endIndex = matchVar.ref().and(matchVar.ref().dotAccess("index"), translationContext.codeGenerator());
    CodeChunk.WithValue unescape = GOOG_STRING_UNESCAPE_ENTITIES.call(translationVar.dotAccess("substring").call(lastIndexVar.ref(), endIndex));
    doBody.add(INCREMENTAL_DOM_TEXT.call(unescape));
    doBody.add(lastIndexVar.ref().assign(regexVar.ref().dotAccess("lastIndex")));
    // switch (match_XXX && match_XXX[0]) { ... }
    SwitchBuilder switchBuilder = CodeChunk.switch_(matchVar.ref().and(matchVar.ref().bracketAccess(CodeChunk.number(0)), translationContext.codeGenerator()));
    for (Map.Entry<String, MsgPlaceholderNode> ph : placeholderNames.entrySet()) {
        switchBuilder.case_(CodeChunk.stringLiteral(ph.getKey()), master.visitForUseByAssistantsAsCodeChunk(ph.getValue()));
    }
    doBody.add(switchBuilder.build());
    return CodeChunk.statements(CodeChunk.statements(translationVar.initialStatements()), regexVar, lastIndexVar, matchVar, DoWhile.builder().setCondition(matchVar.ref()).setBody(CodeChunk.statements(doBody)).build());
}
Also used : CodeChunk(com.google.template.soy.jssrc.dsl.CodeChunk) SwitchBuilder(com.google.template.soy.jssrc.dsl.SwitchBuilder) ArrayList(java.util.ArrayList) VariableDeclaration(com.google.template.soy.jssrc.dsl.VariableDeclaration) Map(java.util.Map) MsgPlaceholderNode(com.google.template.soy.soytree.MsgPlaceholderNode)

Example 2 with SwitchBuilder

use of com.google.template.soy.jssrc.dsl.SwitchBuilder in project closure-templates by google.

the class GenJsCodeVisitor method visitSwitchNode.

/**
 * Example:
 *
 * <pre>
 *   {switch $boo}
 *     {case 0}
 *       ...
 *     {case 1, 2}
 *       ...
 *     {default}
 *       ...
 *   {/switch}
 * </pre>
 *
 * might generate
 *
 * <pre>
 *   switch (opt_data.boo) {
 *     case 0:
 *       ...
 *       break;
 *     case 1:
 *     case 2:
 *       ...
 *       break;
 *     default:
 *       ...
 *   }
 * </pre>
 */
@Override
protected void visitSwitchNode(SwitchNode node) {
    CodeChunk.WithValue switchOn = coerceTypeForSwitchComparison(node.getExpr());
    SwitchBuilder switchBuilder = switch_(switchOn);
    for (SoyNode child : node.getChildren()) {
        if (child instanceof SwitchCaseNode) {
            SwitchCaseNode scn = (SwitchCaseNode) child;
            ImmutableList.Builder<CodeChunk.WithValue> caseChunks = ImmutableList.builder();
            for (ExprNode caseExpr : scn.getExprList()) {
                CodeChunk.WithValue caseChunk = translateExpr(caseExpr);
                caseChunks.add(caseChunk);
            }
            CodeChunk body = visitChildrenReturningCodeChunk(scn);
            switchBuilder.case_(caseChunks.build(), body);
        } else if (child instanceof SwitchDefaultNode) {
            CodeChunk body = visitChildrenReturningCodeChunk((SwitchDefaultNode) child);
            switchBuilder.default_(body);
        } else {
            throw new AssertionError();
        }
    }
    jsCodeBuilder.append(switchBuilder.build());
}
Also used : ExprNode(com.google.template.soy.exprtree.ExprNode) ParentSoyNode(com.google.template.soy.soytree.SoyNode.ParentSoyNode) SoyNode(com.google.template.soy.soytree.SoyNode) CodeChunk(com.google.template.soy.jssrc.dsl.CodeChunk) SwitchBuilder(com.google.template.soy.jssrc.dsl.SwitchBuilder) ImmutableList(com.google.common.collect.ImmutableList) SwitchCaseNode(com.google.template.soy.soytree.SwitchCaseNode) SwitchDefaultNode(com.google.template.soy.soytree.SwitchDefaultNode)

Aggregations

CodeChunk (com.google.template.soy.jssrc.dsl.CodeChunk)2 SwitchBuilder (com.google.template.soy.jssrc.dsl.SwitchBuilder)2 ImmutableList (com.google.common.collect.ImmutableList)1 ExprNode (com.google.template.soy.exprtree.ExprNode)1 VariableDeclaration (com.google.template.soy.jssrc.dsl.VariableDeclaration)1 MsgPlaceholderNode (com.google.template.soy.soytree.MsgPlaceholderNode)1 SoyNode (com.google.template.soy.soytree.SoyNode)1 ParentSoyNode (com.google.template.soy.soytree.SoyNode.ParentSoyNode)1 SwitchCaseNode (com.google.template.soy.soytree.SwitchCaseNode)1 SwitchDefaultNode (com.google.template.soy.soytree.SwitchDefaultNode)1 ArrayList (java.util.ArrayList)1 Map (java.util.Map)1