View Javadoc

1   ////////////////////////////////////////////////////////////////////////////////
2   // MillScript: an Open Spice interpreter and batch website creation tool
3   // Copyright (C) 2001-2004 Open World Ltd
4   //
5   // This file is part of MillScript.
6   //
7   // MillScript is free software; you can redistribute it and/or modify it under
8   // the terms of the GNU General Public License as published by the Free
9   // Software Foundation; either version 2 of the License, or (at your option)
10  // any later version.
11  //
12  // MillScript is distributed in the hope that it will be useful, but WITHOUT
13  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  // more details.
16  //
17  // You should have received a copy of the GNU General Public License along with
18  // MillScript; if not, write to the Free Software Foundation, Inc., 59 Temple
19  // Place, Suite 330, Boston, MA  02111-1307  USA
20  ////////////////////////////////////////////////////////////////////////////////
21  package org.millscript.millscript.expr;
22  
23  import org.millscript.commons.util.IList;
24  import org.millscript.millscript.action.Action;
25  import org.millscript.millscript.action.ForAction;
26  import org.millscript.millscript.action.ForConditionAction;
27  import org.millscript.millscript.vm.CompilerState;
28  
29  import java.util.Stack;
30  
31  /**
32   * This class represents a For loop expression. A for loop expression may
33   * contain multiple condition/binding expressions, a single body expression and
34   * a single termination expression.
35   *
36   * @see org.millscript.millscript.syntax.ForSyntax
37   * @see ForAction
38   */
39  public final class ForExpr extends Expr< ForAction > {
40  
41      /**
42       * This list holds the conditions and bindings that control this for loops
43       * iteration.
44       */
45      private final IList< ForConditionExpr > bindings;
46  
47      /**
48       * The for loops body expression. This will be executed on each iteration of
49       * the for loop.
50       */
51      private final Expr< ? > body;
52  
53      /**
54       * The for loops termination expression. This will be executed when the for
55       * loop terminates, unless one of the other conditions has an override.
56       */
57      private final Expr< ? > terminateExpr;
58  
59      /**
60       * Construct a new For Expression with the supplied conditions/bindings,
61       * body and normal termination expression.
62       *
63       * @param   a   list of conditions and bindings
64       * @param   b   body Expr
65       * @param   t   normal termination Expr
66       */
67      public ForExpr( final IList< ForConditionExpr > a, final Expr< ? > b, final Expr< ? > t ) {
68          bindings = a;
69          body = b;
70          terminateExpr = t;
71      }
72  
73      /**
74       * {@inheritDoc}
75       *
76       * <p>
77       * If the for loop body has an arity of zero, the for loops arity is zero,
78       * otherwise the for loop arity is unknown. The for loop arity would depend
79       * on the number of iterations, etc.
80       * </p>
81       */
82      @Override
83      public int arity() {
84          return body.arity() == 0 ? 0 : -1;
85      }
86  
87      /**
88       * @see org.millscript.millscript.expr.Expr#compileIt()
89       */
90      @Override
91      public ForAction compileIt() {
92  
93          // we compile the termination expression once...
94          final Action finalAct = terminateExpr.compile();
95  
96          // ... and then compile each condition/binding, passing each one the
97          // same compiled termination expression
98          ForConditionAction[] fa = new ForConditionAction[ this.bindings.size() ];
99          for ( int i = 0; i < this.bindings.size(); i++ ) {
100             ForConditionExpr< ? > b = this.bindings.get0( i );
101             fa[ i ] = b.compile();
102             fa[ i ].setFinallyAction( finalAct );
103         }
104         return new ForAction( fa, body.compile() );
105     }
106 
107     /**
108      * @see org.millscript.millscript.expr.Expr#resolve(org.millscript.millscript.vm.CompilerState)
109      *
110      * <p>
111      * In a for expression, the renaming process is slightly more compilcated
112      * than usual. The renaming uses the following sequence:
113      * <ol>
114      * <li>binding expressions children are pushed onto the stack</li>
115      * <li>binding expressions are renamed</li>
116      * <li>the termination expression is renamed</li>
117      * <li>a new scope is declared
118      *   <ol>
119      *   <li>any bound variables are pushed onto the stack</li>
120      *   <li>remaining for conditions are renamed</li>
121      *   <li>for loop body is renamed</li>
122      *   </ol>
123      * </li>
124      * </ol>
125      * Which ensures that the relevant bindings are available within the
126      * condition and body expression.
127      * </p>
128      */
129     @Override
130     public void resolve( final CompilerState state ) {
131         // Rename the expressions in the bindings
132         // e.g <name> in <expr>, we are renaming the <expr>
133         // NOTE - This phase only renames the binding expressions, not the
134         //        condition expressions, such as while, until or where.
135         for ( int i = 1; i <= this.bindings.size(); i++ ) {
136             ForConditionExpr e = this.bindings.get( i );
137             if ( e instanceof BindingExpr ) {
138                 e.resolve( state );
139             }
140         }
141 
142         // Rename the termination expression
143         // This expression cannot refer to loop bindings, what would their value
144         // be when the loop exits or if the loop never executes at all.
145         // NOTE - This caused problems when first done as on refering to a loop
146         //        variable in the termination expression, it returned null.
147         terminateExpr.resolve( state );
148 
149         // Now declare the loop variables and rename the body
150         // e.g. <name> in <expr>, we are declaring the <name> as a local
151         // variable
152         Stack< Expr > scopes = state.getScopes();
153         int n = scopes.size();
154         for ( int i = 1; i <= this.bindings.size(); i++ ) {
155             ForConditionExpr< ? > e = this.bindings.get( i );
156             if ( e instanceof BindingExpr ) {
157                 ((BindingExpr< ? >) e).pushNames( scopes );
158             }
159         }
160         state.declareLocalsFrom( n );
161 
162         // Now that we have declared the loop variables, we can safely rename
163         // the for conditions, e.g. while <expr> else <expr>, as these may
164         // refer back to loop variables
165         for ( int i = 1; i <= this.bindings.size(); i++ ) {
166             ForConditionExpr e = this.bindings.get( i );
167             if ( !( e instanceof BindingExpr ) ) {
168                 // Rename the ForConditions now that the local variables
169                 // have been declared.
170                 e.resolve( state );
171             }
172         }
173 
174         // Rename the body expression
175         body.resolve( state );
176         scopes.setSize( n );
177     }
178 
179     /**
180      * @see org.millscript.millscript.expr.Expr#showComponents(int)
181      */
182     @Override
183     void showComponents( final int n ) {
184         for ( int i = 1; i <= this.bindings.size(); i++ ) {
185             ForConditionExpr e = this.bindings.get( i );
186             e.show( n );
187         }
188         this.body.show( n );
189         this.terminateExpr.show( n );
190     }
191 
192 }