Home | History | Annotate | Download | only in syntax
      1 /*
      2  * Copyright (C) 2010 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.clearsilver.jsilver.syntax;
     18 
     19 import com.google.clearsilver.jsilver.syntax.analysis.DepthFirstAdapter;
     20 import com.google.clearsilver.jsilver.syntax.node.AAltCommand;
     21 import com.google.clearsilver.jsilver.syntax.node.ACallCommand;
     22 import com.google.clearsilver.jsilver.syntax.node.ADataCommand;
     23 import com.google.clearsilver.jsilver.syntax.node.ADefCommand;
     24 import com.google.clearsilver.jsilver.syntax.node.AEachCommand;
     25 import com.google.clearsilver.jsilver.syntax.node.AEvarCommand;
     26 import com.google.clearsilver.jsilver.syntax.node.AHardIncludeCommand;
     27 import com.google.clearsilver.jsilver.syntax.node.AHardLincludeCommand;
     28 import com.google.clearsilver.jsilver.syntax.node.AIfCommand;
     29 import com.google.clearsilver.jsilver.syntax.node.AIncludeCommand;
     30 import com.google.clearsilver.jsilver.syntax.node.ALincludeCommand;
     31 import com.google.clearsilver.jsilver.syntax.node.ALoopCommand;
     32 import com.google.clearsilver.jsilver.syntax.node.ALoopIncCommand;
     33 import com.google.clearsilver.jsilver.syntax.node.ALoopToCommand;
     34 import com.google.clearsilver.jsilver.syntax.node.ALvarCommand;
     35 import com.google.clearsilver.jsilver.syntax.node.ANameCommand;
     36 import com.google.clearsilver.jsilver.syntax.node.AUvarCommand;
     37 import com.google.clearsilver.jsilver.syntax.node.AVarCommand;
     38 import com.google.clearsilver.jsilver.syntax.node.AWithCommand;
     39 import com.google.clearsilver.jsilver.syntax.node.EOF;
     40 import com.google.clearsilver.jsilver.syntax.node.TData;
     41 
     42 import java.util.ArrayList;
     43 import java.util.List;
     44 
     45 /**
     46  * Consolidates runs of (unescaped literal output) data commands, deferring output until another
     47  * output command (var, call, etc) is encountered.
     48  */
     49 public class DataCommandConsolidator extends DepthFirstAdapter {
     50   /**
     51    * The current block nesting level. This is incremented whenever a conditional command is
     52    * encountered.
     53    */
     54   private int currentBlockNestingLevel = 0;
     55   /**
     56    * A list of the data commands we're currently considering for consolidation.
     57    */
     58   private final List<ADataCommand> datas = new ArrayList<ADataCommand>();
     59   /** The block nesting level of the data commands above. */
     60   private int datasBlockNestingLevel = -1;
     61 
     62   /**
     63    * Data consolidation barrier: consolidates all data contents into the last data command in the
     64    * datas list, replacing all but the last node with no-ops.
     65    */
     66   private void barrier() {
     67     if (datas.size() > 1) {
     68       // Put aside the last data command for later, then remove all the other
     69       // data commands, coalescing their contents into the last command.
     70       ADataCommand last = datas.remove(datas.size() - 1);
     71 
     72       StringBuilder sb = new StringBuilder();
     73       for (ADataCommand data : datas) {
     74         sb.append(data.getData().getText());
     75         data.replaceBy(null); // removes the node
     76       }
     77 
     78       sb.append(last.getData().getText());
     79       last.replaceBy(new ADataCommand(new TData(sb.toString())));
     80     }
     81     datas.clear();
     82     datasBlockNestingLevel = -1;
     83   }
     84 
     85   /** Block entry: just increments the current block nesting level. */
     86   private void blockEntry() {
     87     assert datasBlockNestingLevel <= currentBlockNestingLevel;
     88     ++currentBlockNestingLevel;
     89   }
     90 
     91   /**
     92    * Block exit: acts as a conditional barrier only to data contained within the block.
     93    */
     94   private void blockExit() {
     95     assert datasBlockNestingLevel <= currentBlockNestingLevel;
     96     if (datasBlockNestingLevel == currentBlockNestingLevel) {
     97       barrier();
     98     }
     99     --currentBlockNestingLevel;
    100   }
    101 
    102   // data commands: continue to accumulate as long as the block nesting level
    103   // is unchanged.
    104 
    105   @Override
    106   public void caseADataCommand(ADataCommand node) {
    107     assert datasBlockNestingLevel <= currentBlockNestingLevel;
    108     if (currentBlockNestingLevel != datasBlockNestingLevel) {
    109       barrier();
    110     }
    111     datas.add(node);
    112     datasBlockNestingLevel = currentBlockNestingLevel;
    113   }
    114 
    115   // var, lvar, evar, uvar, name: all unconditional barriers.
    116 
    117   @Override
    118   public void inAVarCommand(AVarCommand node) {
    119     barrier();
    120   }
    121 
    122   @Override
    123   public void inALvarCommand(ALvarCommand node) {
    124     barrier();
    125   }
    126 
    127   @Override
    128   public void inAUvarCommand(AUvarCommand node) {
    129     barrier();
    130   }
    131 
    132   @Override
    133   public void inAEvarCommand(AEvarCommand node) {
    134     barrier();
    135   }
    136 
    137   @Override
    138   public void inANameCommand(ANameCommand node) {
    139     barrier();
    140   }
    141 
    142   // loop, each: block barriers.
    143 
    144   @Override
    145   public void inALoopCommand(ALoopCommand node) {
    146     blockEntry();
    147   }
    148 
    149   @Override
    150   public void inALoopIncCommand(ALoopIncCommand node) {
    151     blockEntry();
    152   }
    153 
    154   @Override
    155   public void inALoopToCommand(ALoopToCommand node) {
    156     blockEntry();
    157   }
    158 
    159   @Override
    160   public void inAEachCommand(AEachCommand node) {
    161     blockEntry();
    162   }
    163 
    164   @Override
    165   public void inAWithCommand(AWithCommand node) {
    166     blockEntry();
    167   }
    168 
    169   @Override
    170   public void outALoopCommand(ALoopCommand node) {
    171     blockExit();
    172   }
    173 
    174   @Override
    175   public void outALoopIncCommand(ALoopIncCommand node) {
    176     blockExit();
    177   }
    178 
    179   @Override
    180   public void outALoopToCommand(ALoopToCommand node) {
    181     blockExit();
    182   }
    183 
    184   @Override
    185   public void outAEachCommand(AEachCommand node) {
    186     blockExit();
    187   }
    188 
    189   @Override
    190   public void outAWithCommand(AWithCommand node) {
    191     blockExit();
    192   }
    193 
    194   // def: special case: run another instance of this optimizer on the contained
    195   // commands. def produces no output, so it should not act as a barrier to
    196   // any accumulated data nodes; however, it contains data nodes, so we can
    197   // (and should) consolidate them.
    198 
    199   @Override
    200   public void caseADefCommand(ADefCommand node) {
    201     DataCommandConsolidator consolidator = new DataCommandConsolidator();
    202     node.getCommand().apply(consolidator);
    203     consolidator.barrier(); // Force final consolidation, just like EOF would.
    204   }
    205 
    206   // call: unconditional barrier.
    207 
    208   @Override
    209   public void inACallCommand(ACallCommand node) {
    210     barrier();
    211   }
    212 
    213   // if: special case: each branch is a block barrier.
    214 
    215   @Override
    216   public void caseAIfCommand(AIfCommand node) {
    217     if (node.getBlock() != null) {
    218       blockEntry();
    219       node.getBlock().apply(this);
    220       blockExit();
    221     }
    222     if (node.getOtherwise() != null) {
    223       blockEntry();
    224       node.getOtherwise().apply(this);
    225       blockExit();
    226     }
    227   }
    228 
    229   // alt: block barrier.
    230 
    231   @Override
    232   public void inAAltCommand(AAltCommand node) {
    233     blockEntry();
    234   }
    235 
    236   @Override
    237   public void outAAltCommand(AAltCommand node) {
    238     blockExit();
    239   }
    240 
    241   // include, hard include, linclude, hard linclude unconditional barriers.
    242 
    243   @Override
    244   public void caseAIncludeCommand(AIncludeCommand node) {
    245     barrier();
    246   }
    247 
    248   @Override
    249   public void caseAHardIncludeCommand(AHardIncludeCommand node) {
    250     barrier();
    251   }
    252 
    253   @Override
    254   public void caseALincludeCommand(ALincludeCommand node) {
    255     barrier();
    256   }
    257 
    258   @Override
    259   public void caseAHardLincludeCommand(AHardLincludeCommand node) {
    260     barrier();
    261   }
    262 
    263   // EOF: unconditional barrier.
    264 
    265   @Override
    266   public void caseEOF(EOF node) {
    267     barrier();
    268   }
    269 }
    270