PageRenderTime 64ms CodeModel.GetById 3ms app.highlight 56ms RepoModel.GetById 1ms app.codeStats 0ms

/src/org/charter_project/graph/compile/statement/ForEach.java

https://bitbucket.org/aicas/rdt
Java | 557 lines | 428 code | 52 blank | 77 comment | 35 complexity | 8c00b1d0b180ee415cfec440dd2bc447 MD5 | raw file
  1package org.charter_project.graph.compile.statement;
  2
  3import org.charter_project.graph.client.ExpressionType;
  4import org.charter_project.graph.client.GenerateException;
  5import org.charter_project.graph.compile.Code.FailCode;
  6import org.charter_project.graph.compile.LoopCode;
  7import org.charter_project.graph.compile.Parsed;
  8import org.charter_project.graph.compile.RuleFile;
  9import org.charter_project.graph.compile.block.Block;
 10import org.charter_project.graph.compile.block.Block.BlockType;
 11import org.charter_project.graph.compile.expression.Cast;
 12import org.charter_project.graph.compile.expression.Expression;
 13import org.charter_project.graph.compile.expression.GetSize;
 14import org.charter_project.graph.compile.expression.Literal;
 15import org.charter_project.graph.compile.expression.SelectMField;
 16import org.charter_project.graph.compile.expression.Var;
 17import org.charter_project.graph.parse.MyTree;
 18
 19/**
 20 * A <code>ForEach</code> represents the 'foreach' alternative of a
 21 * {@link Statement}.
 22 *
 23 * @author Maarten de Mol
 24 * @version 1.0
 25 */
 26public class ForEach extends Statement implements AssignRHS
 27{
 28  // The for variable.
 29  private final Var        loopVar;
 30
 31  // The collection expression. May be null.
 32  private final Expression coll;
 33
 34  // The body.
 35  private final Block      body;
 36
 37  // The collects expression. May be null.
 38  private final Expression collects;
 39
 40  // Additional flag to disable initial comment line.
 41  private boolean          disableComment = false;
 42
 43  /** Constructor for expressions provided by ANTLR. */
 44  public ForEach(MyTree tree, Var loopVar, Expression coll, Block body,
 45                 Expression collects)
 46  {
 47    super(tree);
 48    this.loopVar = loopVar;
 49    this.coll = coll;
 50    this.body = body;
 51    this.collects = collects;
 52  }
 53
 54  /** Constructor for custom expressions, created during code generation. */
 55  public ForEach(Parsed source, Var loopVar, Expression coll, Block body,
 56                 Expression collects)
 57  {
 58    super(source);
 59    this.loopVar = loopVar;
 60    this.coll = coll;
 61    this.body = body;
 62    this.collects = collects;
 63  }
 64
 65  /** Getter for the loop variable. */
 66  public Var getLoopVar()
 67  {
 68    return this.loopVar;
 69  }
 70
 71  /** Getter for the collection expression. */
 72  public Expression getCollection()
 73  {
 74    return this.coll;
 75  }
 76
 77  /** Getter for the body. */
 78  public Block getBody()
 79  {
 80    return this.body;
 81  }
 82
 83  /** Getter for the return expression. */
 84  public Expression getCollects()
 85  {
 86    return this.collects;
 87  }
 88
 89  /** Setter for the disable comment flag. */
 90  public void disableComment()
 91  {
 92    this.disableComment = true;
 93  }
 94
 95  @Override
 96  public ExpressionType getType()
 97  {
 98    return this.collects.getType();
 99  }
100
101  @Override
102  public void show(Indented builder)
103  {
104    builder.write("foreach (");
105    builder.write(this.loopVar.getType().toString());
106    builder.write(" ");
107    builder.write(this.loopVar.getName());
108    if (this.coll != null)
109      {
110        builder.write(" : ");
111        builder.write(this.coll.show(false));
112      }
113    builder.write(") ");
114    builder.write("{");
115    builder.incIndent();
116    builder.newline();
117    this.body.show(builder, false);
118    if (this.collects != null)
119      {
120        builder.newline();
121        builder.write("return ");
122        builder.write(this.collects.show(false));
123        builder.write(";");
124      }
125    builder.decIndent();
126    builder.newline();
127    builder.write("}");
128  }
129
130  // ========================================================================
131  // LOOP CODE
132  // ========================================================================
133
134  /** Generates the {@link LoopCode} for this foreach statement. */
135  public LoopCode generateLoopCode(RuleFile file)
136  {
137    if (this.coll == null)
138      {
139        return new LoopAllCode();
140      }
141    else if (this.coll instanceof SelectMField &&
142             file.inBlockType() != BlockType.SEQUENCE)
143      {
144        SelectMField select = (SelectMField) this.coll;
145        return new LoopFieldCode(select);
146      }
147    else if (this.coll instanceof GetSize)
148      {
149        GetSize getSize = (GetSize) this.coll;
150        return new LoopIndexCode(getSize);
151      }
152    else
153      {
154        return new MyLoopCode();
155      }
156  }
157
158  /**
159   * Helper method for printing a comment for the 'collects' expression at the
160   * end of the loop body.
161   */
162  private static void writeCollectsComment(Expression collects, RuleFile file)
163    throws GenerateException
164  {
165    if (collects != null)
166      {
167        file.newline();
168        file.write("// collect ");
169        file.write(collects.show(false));
170        file.newline();
171      }
172  }
173
174  /**
175   * Helper class for writing loop case, for the case that no collection
176   * expression was provided.
177   */
178  private class LoopAllCode implements LoopCode
179  {
180
181    // Loop code for the bare node visitor.
182    private NodeVisitCode baseLoop;
183
184    @Override
185    public void writeOpen(FailCode fail, RuleFile file)
186      throws GenerateException
187    {
188      this.baseLoop =
189        new NodeVisitCode(ForEach.this, ForEach.this.loopVar.getType()
190            .getNode());
191      if (ForEach.this.collects == null && !ForEach.this.disableComment)
192        {
193          generateComment(file);
194        }
195      this.baseLoop.writeOpen(fail, file);
196      ForEach.this.loopVar.writeAssignCode(this.baseLoop.getValue(), file);
197      ForEach.this.body.write(FailCode.VISITOR_FAIL, file);
198      writeCollectsComment(ForEach.this.collects, file);
199    }
200
201    @Override
202    public void writeClose(boolean ruleCall, boolean endReachable,
203                           FailCode fail, RuleFile file)
204      throws GenerateException
205    {
206      this.baseLoop.writeClose(ruleCall || ForEach.this.body.hasRuleCall(),
207                               endReachable, fail, file);
208    }
209
210    @Override
211    public void writeContinue(RuleFile file)
212      throws GenerateException
213    {
214      this.baseLoop.writeContinue(file);
215    }
216
217    @Override
218    public void writeBreak(RuleFile file)
219      throws GenerateException
220    {
221      this.baseLoop.writeBreak(file);
222    }
223
224    @Override
225    public Expression getValue()
226    {
227      return ForEach.this.collects;
228    }
229  }
230
231  /**
232   * Helper class for writing loop case, for the case that the collection
233   * expression is a field selection.
234   */
235  private class LoopFieldCode implements LoopCode
236  {
237
238    // The select expression, and its generated loop code.
239    private final SelectMField  select;
240    private LoopCode            loop;
241
242    /** Default constructor. */
243    public LoopFieldCode(SelectMField select)
244    {
245      this.select = select;
246    }
247
248    @Override
249    public void writeOpen(FailCode fail, RuleFile file)
250      throws GenerateException
251    {
252      // Comment
253      if (ForEach.this.collects == null && !ForEach.this.disableComment)
254        {
255          generateComment(file);
256        }
257      // Create index variable, if necessary.
258      Var index = null;
259      if (file.getRule().getVarsWithIndex()
260          .contains(ForEach.this.loopVar.getName()) &&
261          select.getType().isList())
262        {
263          index = file.generateClassVar(ForEach.this, ExpressionType.INT);
264          index.writeAssignCode(Literal.MINUS_ONE(ForEach.this).getCode(),
265                                file);
266          file.getIndexVarMap().put(ForEach.this.loopVar.getName(), index);
267        }
268      // Open visitor.
269      this.loop = select.generateLoopCode();
270      this.loop.writeOpen(fail, file);
271      // Body statements (assign loopVar)
272      this.loop.getValue().writeHelperCode(fail, file);
273      if (!ForEach.this.loopVar.getType().equals(this.loop.getValue()
274                                                     .getType()))
275        {
276          // type cast before assign
277          file.write("if (!(");
278          this.loop.getValue().getCode().write(true, file);
279          file.write(" instanceof ");
280          file.write(ForEach.this.loopVar.getType());
281          file.write(")) ");
282          file.openBlock();
283          this.loop.writeContinue(file);
284          file.closeBlock();
285          Cast cast =
286            new Cast(ForEach.this, false, ForEach.this.loopVar.getType(),
287                     this.loop.getValue());
288          cast.writeHelperCode(FailCode.EMPTY, file);
289          ForEach.this.loopVar.writeAssignCode(cast.getCode(), file);
290        }
291      else
292        {
293          // assign as is
294          ForEach.this.loopVar
295              .writeAssignCode(this.loop.getValue().getCode(), file);
296        }
297      // Body statements (increase indexVar).
298      if (index != null)
299        {
300          file.write(index);
301          file.write("++;");
302          file.newline();
303        }
304      // Body statements (for body, collects)
305      ForEach.this.body.write(FailCode.VISITOR_FAIL, file);
306      writeCollectsComment(ForEach.this.collects, file);
307    }
308
309    @Override
310    public void writeClose(boolean ruleCall, boolean endReachable,
311                           FailCode fail, RuleFile file)
312      throws GenerateException
313    {
314      this.loop.writeClose(ruleCall || ForEach.this.body.hasRuleCall(),
315                           endReachable, fail, file);
316    }
317
318    @Override
319    public void writeContinue(RuleFile file)
320      throws GenerateException
321    {
322      this.loop.writeContinue(file);
323    }
324
325    @Override
326    public void writeBreak(RuleFile file)
327      throws GenerateException
328    {
329      this.loop.writeBreak(file);
330    }
331
332    @Override
333    public Expression getValue()
334    {
335      return ForEach.this.collects;
336    }
337  }
338
339  /**
340   * Helper class for writing loop case, for the case that the collection
341   * expression is a getSize operation. This alternative cannot be reached
342   * directly, and is only supported for the 'restrict to indexOf' alternative
343   * in a match block.
344   */
345  private class LoopIndexCode implements LoopCode
346  {
347
348    // The getSize expression.
349    private final GetSize getSize;
350
351    // The label of the generated for loop.
352    private String        label;
353
354    /** Default constructor. */
355    public LoopIndexCode(GetSize getSize)
356    {
357      this.getSize = getSize;
358    }
359
360    @Override
361    public void writeOpen(FailCode fail, RuleFile file)
362      throws GenerateException
363    {
364      // Comment
365      if (ForEach.this.collects == null && !ForEach.this.disableComment)
366        {
367          generateComment(file);
368        }
369      // Variables.
370      Var tempVar =
371        file.generateLocalVar(ForEach.this, ForEach.this.loopVar.getType());
372      this.getSize.writeHelperCode(fail, file);
373      // Open for loop.
374      this.label = file.freshLabel();
375      file.write(label);
376      file.write(": for (int ");
377      file.write(tempVar);
378      file.write("= 0; ");
379      file.write(tempVar);
380      file.write(" < ");
381      this.getSize.getCode().write(true, file);
382      file.write("; ");
383      file.write(tempVar);
384      file.write("++) ");
385      file.openBlock();
386      // Body statements.
387      ForEach.this.loopVar.writeAssignCode(tempVar, file);
388      ForEach.this.body.write(FailCode.VISITOR_FAIL, file);
389      writeCollectsComment(ForEach.this.collects, file);
390    }
391
392    @Override
393    public void writeClose(boolean ruleCall, boolean endReachable,
394                           FailCode fail, RuleFile file)
395      throws GenerateException
396    {
397      file.closeBlock();
398    }
399
400    @Override
401    public void writeContinue(RuleFile file)
402      throws GenerateException
403    {
404      file.write("continue ");
405      file.write(this.label);
406      file.write(";");
407      file.newline();
408    }
409
410    @Override
411    public void writeBreak(RuleFile file)
412      throws GenerateException
413    {
414      file.write("break ");
415      file.write(this.label);
416      file.write(";");
417      file.newline();
418    }
419
420    @Override
421    public Expression getValue()
422    {
423      return ForEach.this.collects;
424    }
425  }
426
427  /**
428   * Helper class for writing loop case, for the case that the collection
429   * expression cannot be decomposed.
430   */
431  private class MyLoopCode implements LoopCode
432  {
433
434    // Label of the for loop.
435    private String label;
436
437    @Override
438    public void writeOpen(FailCode fail, RuleFile file)
439      throws GenerateException
440    {
441      // Comment
442      if (ForEach.this.collects == null && !ForEach.this.disableComment)
443        {
444          generateComment(file);
445        }
446      // Variables.
447      Var tempVar =
448        file.generateLocalVar(ForEach.this, ForEach.this.coll.getType()
449            .getElement());
450      // Helper code.
451      ForEach.this.coll.writeHelperCode(fail, file);
452      // Create index variable, if necessary.
453      Var index = null;
454      if (file.getRule().getVarsWithIndex()
455          .contains(ForEach.this.loopVar.getName()) &&
456          ForEach.this.coll.getType().isList())
457        {
458          index = file.generateClassVar(ForEach.this, ExpressionType.INT);
459          index.writeAssignCode(Literal.MINUS_ONE(ForEach.this).getCode(),
460                                file);
461          file.getIndexVarMap().put(ForEach.this.loopVar.getName(), index);
462        }
463      // Open for loop
464      this.label = file.freshLabel();
465      file.write(label);
466      file.write(": for (");
467      file.write(tempVar.getType());
468      file.write(" ");
469      file.write(tempVar.getName());
470      file.write(" : ");
471      ForEach.this.coll.getCode().write(false, file);
472      file.write(") ");
473      file.openBlock();
474      // Body statements (assign loopVar)
475      if (!ForEach.this.loopVar.getType().equals(tempVar.getType()))
476        {
477          // type cast before assign
478          file.write("if (!(");
479          file.write(tempVar);
480          file.write(" instanceof ");
481          file.write(ForEach.this.loopVar.getType());
482          file.write(")) ");
483          file.openBlock();
484          writeContinue(file);
485          file.closeBlock();
486          Cast cast =
487            new Cast(ForEach.this, false, ForEach.this.loopVar.getType(),
488                     tempVar);
489          cast.writeHelperCode(FailCode.EMPTY, file);
490          ForEach.this.loopVar.writeAssignCode(cast.getCode(), file);
491        }
492      else
493        {
494          ForEach.this.loopVar.writeAssignCode(tempVar, file);
495        }
496
497      // Body statements (increase indexVar).
498      if (index != null)
499        {
500          file.write(index);
501          file.write("++;");
502          file.newline();
503        }
504      // Body statements (for body, collects)
505      ForEach.this.body.write(fail, file);
506      writeCollectsComment(ForEach.this.collects, file);
507    }
508
509    @Override
510    public void writeClose(boolean ruleCall, boolean endReachable,
511                           FailCode fail, RuleFile file)
512      throws GenerateException
513    {
514      file.closeBlock();
515    }
516
517    @Override
518    public void writeContinue(RuleFile file)
519      throws GenerateException
520    {
521      file.write("continue ");
522      file.write(this.label);
523      file.write(";");
524      file.newline();
525    }
526
527    @Override
528    public void writeBreak(RuleFile file)
529      throws GenerateException
530    {
531      file.write("break ");
532      file.write(this.label);
533      file.write(";");
534      file.newline();
535    }
536
537    @Override
538    public Expression getValue()
539    {
540      return ForEach.this.collects;
541    }
542  }
543
544  // ========================================================================
545  // CODE GENERATION
546  // ========================================================================
547
548  @Override
549  public void write(FailCode fail, RuleFile file)
550    throws GenerateException
551  {
552    LoopCode loop = generateLoopCode(file);
553    loop.writeOpen(fail, file);
554    // collects is not supported in this mode
555    loop.writeClose(false, true, fail, file);
556  }
557}