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 <condition1>; <condition2>; ... <conditionN> do
46 * ...
47 * finally
48 * ...
49 * endfor
50 *
51 * <condition> ::= where <expr>
52 * ::= while <expr> else <expr>
53 * ::= until <expr> then <expr>
54 * <binding> ::= condition
55 * ::= <name> in <expr>
56 * ::= <name> & <name> in <expr>
57 * ::= <name> from <expr> [ to <expr> ]
58 * ::= <name> from <expr> [ downto <expr> ] <-- 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 }