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.SwitchAction;
26  import org.millscript.millscript.vm.CompilerState;
27  
28  /**
29   * This class implements a <code>switch</code> expression.
30   *
31   * @see org.millscript.millscript.syntax.SwitchSyntax
32   * @see SwitchAction
33   */
34  public final class SwitchExpr extends Expr< SwitchAction > {
35  
36      /**
37       * The expression representing our choice. This will be compared against the
38       * individual case expressions to find a match.
39       */
40      private final Expr< ? > choice;
41  
42      /**
43       * The list of case expressions.
44       */
45      private final IList< CaseExpr > cases;
46  
47      /**
48       * The default block to execute if no cases match.
49       */
50      private final Expr< ? > def;
51  
52      /**
53       * Creates a new <code>switch</code> expression for the specified choice,
54       * case and default expressions.
55       *
56       * @param   a   the choice expression
57       * @param   b   the list of case expressions
58       * @param   c   the default expression
59       */
60      public SwitchExpr( final Expr< ? > a, final IList< CaseExpr > b, final Expr< ? > c ) {
61          // The choice expression must return a single result
62          this.choice = CheckExpr.make( a );
63          // Set the list of cases
64          this.cases = b;
65          // Set the default expression. If null is specified, then set it to a
66          // skip expression
67          this.def = c == null ? new SkipExpr() : c;
68      }
69  
70      /**
71       * @see org.millscript.millscript.expr.Expr#arity()
72       */
73      @Override
74      public int arity() {
75          // For a <code>switch</code> expression, the arity is only known
76          // exactly if the arity of each case expression body and default
77          // expression are the same.
78          int a = def.arity();
79          for ( int i = 1; i <= this.cases.size(); i++ ) {
80              final CaseExpr ce = this.cases.get( i );
81              if ( ce.getAct().arity() != a ) {
82                  return -1;
83              }
84          }
85          return a;
86      }
87  
88      /**
89       * @see org.millscript.millscript.expr.Expr#compileIt()
90       */
91      @Override
92      public SwitchAction compileIt() {
93  
94          // How many separate patterns are there?
95          int count = 0;
96          for ( int i = 1; i <= this.cases.size(); i++ ) {
97              count += this.cases.get( i ).getPatterns().size();
98          }
99  
100         // This will hold all the compiled patterns for this instances case
101         // expressions
102         final Action[] preds = new Action[ count ];
103         // This keeps track of each compiled pattern and which case expression
104         // it relates to, hence which compiled body expression
105         final int[] switches = new int[ count ];
106         // This will hold all the complied bodies for this instances case
107         // expressions
108         final Action[] acts = new Action[ cases.size() ];
109         // TODO - I think having both switches and acts is overkill. Why not
110         // make switches simply point to the required case action?
111 
112         // i counts the case expressions, k counts the patterns
113         for ( int i = 0, k = 0; i < this.cases.size(); i++ ) {
114             final CaseExpr ce = this.cases.get0( i );
115             // Iterate over each pattern for the case expression
116             for ( int p = 0; p < ce.getPatterns().size(); p++, k++ ) {
117                 final Expr< ? > patn = ce.getPatterns().get0( p );
118                 // Compile the pattern
119                 preds[ k ] = patn.compile();
120                 // Store the body expression this pattern relates to, e.g. a
121                 // pointer into the acts array.
122                 switches[ k ] = i;
123             }
124             // Compile the case expressions body
125             acts[ i ] = ce.getAct().compile();
126         }
127         return new SwitchAction( choice.compile(), preds, switches, acts, def.compile() );
128     }
129 
130     /**
131      * @see org.millscript.millscript.expr.Expr#resolve(org.millscript.millscript.vm.CompilerState)
132      */
133     @Override
134     public void resolve( final CompilerState state ) {
135         this.choice.resolve( state );
136         Expr.resolveList( state, this.cases );
137         this.def.resolve( state );
138     }
139 
140     /**
141      * @see org.millscript.millscript.expr.Expr#showComponents(int)
142      */
143     @Override
144     void showComponents( final int n ) {
145         this.choice.show( n );
146         for ( int i = 1; i <= this.cases.size(); i++ ) {
147             ((Expr) this.cases.get( i )).show( n );
148         }
149         this.def.show( n );
150     }
151 
152 }