View Javadoc

1   ////////////////////////////////////////////////////////////////////////////////
2   // MillScript: an Open Spice interpreter and batch website creation tool
3   // Copyright (C) 2001-2004 Open World Ltd
4   // Copyright (C) 2005 Kevin Rogers
5   //
6   // This file is part of MillScript.
7   //
8   // MillScript is free software; you can redistribute it and/or modify it under
9   // the terms of the GNU General Public License as published by the Free
10  // Software Foundation; either version 2 of the License, or (at your option)
11  // any later version.
12  //
13  // MillScript is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  // more details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // MillScript; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA  02111-1307  USA
21  ////////////////////////////////////////////////////////////////////////////////
22  package org.millscript.millscript.syntax;
23  
24  import org.millscript.commons.util.EList;
25  import org.millscript.commons.util.list.EArrayList;
26  import org.millscript.millscript.alert.Alerts;
27  import org.millscript.millscript.expr.BindingFromExpr;
28  import org.millscript.millscript.expr.BindingFromToExpr;
29  import org.millscript.millscript.expr.BindingListExpr;
30  import org.millscript.millscript.expr.BindingMapExpr;
31  import org.millscript.millscript.expr.Expr;
32  import org.millscript.millscript.expr.ForConditionExpr;
33  import org.millscript.millscript.expr.ForConditionWhereExpr;
34  import org.millscript.millscript.expr.ForConditionWhileExpr;
35  import org.millscript.millscript.expr.ForExpr;
36  import org.millscript.millscript.expr.NameExpr;
37  import org.millscript.millscript.expr.NotExpr;
38  import org.millscript.millscript.expr.SkipExpr;
39  
40  /**
41   * This class implements <code>for</code> loop syntax.
42   * General syntax is.
43   *
44   * <blockquote>
45   *   for &lt;condition1&gt;; &lt;condition2&gt;; ... &lt;conditionN&gt; do
46   *     ...
47   *   finally
48   *     ...
49   *   endfor
50   *
51   *   &lt;condition&gt; ::= where &lt;expr&gt;
52   *               ::= while &lt;expr&gt; else &lt;expr&gt;
53   *               ::= until &lt;expr&gt; then &lt;expr&gt;
54   *   &lt;binding&gt; ::= condition
55   *               ::= &lt;name&gt; in &lt;expr&gt;
56   *               ::= &lt;name&gt; &#38; &lt;name&gt; in &lt;expr&gt;
57   *               ::= &lt;name&gt; from &lt;expr&gt; [ to &lt;expr&gt; ]
58   *               ::= &lt;name&gt; from &lt;expr&gt; [ downto &lt;expr&gt; ]    &lt;-- not implemented yet
59   * </blockquote>
60   *
61   * @see ForExpr
62   * @see org.millscript.millscript.expr.ForConditionExpr
63   */
64  public final class ForSyntax extends PrefixSyntax {
65  
66      /**
67       * @see org.millscript.millscript.syntax.PrefixSyntaxInterface#prefix(java.lang.String, org.millscript.millscript.syntax.Parser)
68       */
69      @Override
70      public ForExpr prefix( final String sym, final Parser parser ) {
71          // This stack will hold all the conditons/bindings for this for loop
72          EList< ForConditionExpr > bindings = new EArrayList< ForConditionExpr >( 4 );
73          // Check some conditions/bindings have been specified
74          if ( !parser.tryRead( "do" ) ) {
75              // Loop until we've read all the conditions and bindings
76              for (;;) {
77                  // First of all we check for loop conditions
78                  if ( parser.tryRead( "while" ) ) {
79                      // while <expr> else <expr>
80                      // Read the predicate expression
81                      Expr pred = parser.readExpr();
82                      // Read the optional end expression
83                      Expr end = new SkipExpr();
84                      if ( parser.tryRead( "else" ) ) {
85                          end = parser.readExpr();
86                      }
87                      // Push this condition onto the stack
88                      bindings.addLast( new ForConditionWhileExpr( pred, end ) );
89                  } else if ( parser.tryRead( "until" ) ) {
90                      // until <expr> then <expr>
91                      // Read the predicate expression
92                      Expr pred = parser.readExpr();
93                      // Invert it
94                      pred = NotExpr.make( pred );
95                      // Read the optional end expression
96                      Expr end = new SkipExpr();
97                      if ( parser.tryRead( "then" ) ) {
98                          end = parser.readExpr();
99                      }
100                     // Push this condition onto the stack
101                     bindings.addLast( new ForConditionWhileExpr( pred, end ) );
102                 } else {
103                     // Having checked for loop conditions, we must check for
104                     // bindings.
105                     // <name> ...
106                     NameExpr name = parser.readName();
107                     if ( parser.tryRead( "in" ) ) {
108                         // ... in <expr>
109                         // Read the expression to iterate over
110                         Expr obj = parser.readExpr();
111                         // Push this binding onto the stack
112                         bindings.addLast( new BindingListExpr( name, obj ) );
113                     } else if ( parser.tryRead( "&" ) ) {
114                         // ... & <name> in <expr>
115                         // Read the other name expression
116                         NameExpr val = parser.readName();
117                         // Now we must read "in"
118                         parser.mustRead( "in" );
119                         // Read the expression to iterate over
120                         Expr obj = parser.readExpr();
121                         // Push this binding onto the stack
122                         bindings.addLast( new BindingMapExpr( name, val, obj ) );
123                     } else if ( parser.tryRead( "from" ) ) {
124                         // ... from <expr> ...
125                         // Read the expression to start iterating from
126                         Expr fromObj = parser.readExpr();
127                         // Check for the optional expression to end iteration at
128                         if ( parser.tryRead( "to" ) ) {
129                             // ... to <expr>
130                             // Read the expression to end iteration at
131                             Expr toObj = parser.readExpr();
132                             // Push this binding onto the stack
133                             bindings.addLast(
134                                 new BindingFromToExpr( name, fromObj, toObj )
135                             );
136                         } else {
137                             // Push this binding onto the stack
138                             bindings.addLast(
139                                 new BindingFromExpr( name, fromObj )
140                             );
141                         }
142                     } else {
143                         // Unexpected condition or binding
144                         throw(
145                             Alerts.parse(
146                                 "Unexpected binding in for loop",
147                                 "Must be either 'in' or 'from'."
148                             ).culprit( "token", parser.getErrorString() ).mishap()
149                         );
150                     }
151                     // This ensures the "where" clause should only ever appear
152                     // after a binding, which we've just read...
153                     if ( parser.tryRead( "where" ) ) {
154                         Expr whereExpr = parser.readExpr();
155                         bindings.addLast( new ForConditionWhereExpr( whereExpr ) );
156                     }
157                 }
158                 // If we don't read a semi-colon separating more
159                 // conditions/bindings, we must read the "do" indicating the
160                 // start of the for loop body.
161                 if ( !( parser.tryRead( ";" ) ) ) {
162                     parser.mustRead( "do" );
163                     break;
164                 }
165             }
166         }
167 
168         // Read the body of the loop
169         Expr body = parser.readStmnts();
170 
171         // Intialise the normal termination expression
172         Expr normalTermination = new SkipExpr();
173 
174         // Try to read the normal termination, i.e. finally, block
175         if ( parser.tryRead( "finally" ) ) {
176             normalTermination = parser.readExpr();
177         }
178 
179         parser.mustRead( "endfor" );
180 
181         // Return a new for loop exression for the parsed syntax
182         return new ForExpr( bindings, body, normalTermination );
183     }
184 
185 }