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.vm;
23  
24  import org.millscript.commons.util.IList;
25  import org.millscript.commons.util.ListIterator;
26  import org.millscript.commons.util.MapIterator;
27  import org.millscript.commons.util.iterator.ArrayListIterator;
28  import org.millscript.commons.util.list.EArrayList;
29  import org.millscript.commons.util.list.IArrayList;
30  import org.millscript.millscript.alert.Alerts;
31  
32  import java.io.Serializable;
33  import java.util.Iterator;
34  
35  /**
36   * This class provides the skeletal implementation for a machine.
37   */
38  public abstract class BasicMachine extends TraceableMachine {
39  
40      /**
41       * This class implements the save/restore point marker for use in the piggy
42       * bank.
43       */
44      private static class StackMark implements Serializable {
45  
46          /**
47           * This is the new ID for the stack mark, which might be useful in the
48           * event a machine is serialized. It should ensure portability between
49           * JVMs.
50           */
51          private static final long serialVersionUID = 3760848956401463600L;
52  
53      }
54  
55      /**
56       * This class is used to mark the last save/restore point in the piggy
57       * bank.
58       */
59      private static final StackMark STACKMARK = new StackMark();
60  
61      /**
62       * This is the equivalent of the call stack in a conventional
63       * implementation.
64       */
65      protected Object[] piggyBank = new Object[ 1024 ];
66  
67      /**
68       * The index of the next value in the piggy bank. Note this is also the
69       * number of values in the stack.
70       */
71      private int piggyBankIndex = 0; 
72      
73      /**
74       * This is an operand stack.
75       */
76      protected Object[] valueStack = new Object[ 1024 ];
77  
78      /**
79       * The index of the next value in the operand stack. Note this is also the
80       * number of values in the stack and hence it is always one greater than
81       * the index of the object in the value stack.
82       */
83      private int valueStackIndex = 0; 
84      
85      /**
86       * Returns the number of objects in the stack.
87       *
88       * @return  the number of objects in the stack
89       */
90      public int getCount() {
91          return this.valueStackIndex;
92      }
93  
94      /**
95       * Returns the object at the specified position in the stack.
96       *
97       * @param n	index of the element to return
98       * @return  the object at the specified position in the stack
99       */
100     public Object getIndex( final int n ) {
101         return this.valueStack[ n ];
102     }
103 
104     /**
105      * Returns the object at the top of the stack. The object is left at it's
106      * current position on the stack.
107      *
108      * @return  the object at the top of the stack
109      */
110     public Object peekObject() {
111         return this.valueStack[ this.valueStackIndex - 1 ];
112     }
113 
114     /**
115      * Replaces the object at the top of the stack with the specified object.
116      *
117      * @param x	the object to put at the top of the stack
118      */
119     public void pokeObject( final Object x ) {
120         this.valueStack[ this.valueStackIndex - 1 ] = x;
121     }
122 
123     /**
124      * Pops the specified number of values from the top of the value stack and
125      * returns them as an array.
126      *
127      * @param nargs	the number of values to pop
128      * @return
129      */
130     public Object[] popArgsArray( final int nargs ) {
131         if ( nargs >= 0 && nargs <= this.valueStackIndex ) {
132             final Object[] args = new Object[ nargs ];
133             for ( int i = nargs - 1; i >=0; i-- ) {
134                 args[ i ] = this.popObject();
135             }
136             return args;
137         } else {
138             if ( nargs < 0 ) {
139                 throw(
140                     Alerts.fault(
141     	                "Compiler bug - trying to construct an array of negative size"
142                     ).mishap()
143                 );
144             } else {
145                 throw(
146                     Alerts.fault(
147     	                "Compiler bug - insufficient values left on the stack"
148                     ).mishap()
149                 );
150             }
151         }
152     }
153 
154     /**
155      * Pops enough arguments off the stack to fill the supplied array. The item
156      * at the top of the stack goes at the end of the array.
157      *
158      * @param args  the array to fill with values from the stack
159      * @return  the supplied array filled with new values
160      */
161     public Object[] popArgsIntoArray( final Object[] args ) {
162         if ( args.length >= 0 && args.length <= this.valueStackIndex ) {
163             for ( int i = args.length - 1; i >=0; i-- ) {
164                 args[ i ] = this.popObject();
165             }
166             return args;
167         } else {
168             if ( args.length < 0 ) {
169                 throw(
170                     Alerts.fault(
171                         "Compiler bug - trying to construct an array of negative size"
172                     ).mishap()
173                 );
174             } else {
175                 throw(
176                     Alerts.fault(
177                         "Compiler bug - insufficient values left on the stack"
178                     ).mishap()
179                 );
180             }
181         }
182     }
183 
184     /**
185      * Pops the specified number of values from the top of the operand stack
186      * and returns an iterator to iterate over them.
187      *
188      * @param nargs	the number of values to pop
189      * @return	an iterator over the poped values
190      */
191     public ListIterator< Object > popArgsIterator( final int nargs ) {
192         return new ArrayListIterator< Object >( this.popArgsArray( nargs ), true );
193     }
194 
195     /**
196      * Pops the specifed number of values from the top of the operand stack and
197      * returns them as a list.
198      *
199      * @param nargs	the number of values to pop
200      * @return  a list of the poped values
201      */
202     public IList< Object > popArgsList( final int nargs, final boolean mutable ) {
203         if ( nargs >= 0 && nargs <= this.valueStackIndex ) {
204             if ( mutable ) {
205                 // Return an immutable list
206                 return new EArrayList< Object >( this.popArgsArray( nargs ), true );
207             } else {
208                 // Return an immutable list
209                 return new IArrayList< Object >( this.popArgsArray( nargs ), true );
210             }
211         } else {
212             throw(
213                 Alerts.fault(
214 	                "Compiler bug - insufficient values left on the stack"
215                 ).mishap()
216             );
217         }
218     }
219 
220     /**
221      * Pops the object off the top of the operand stack and returns it as the
222      * value of this method.
223      *
224      * @return  the object from the top of the operand stack.
225      */
226     public Object popObject() {
227         // Decrement the index before indexing the array. Remember that the
228         // index is the index for the next element and hence one greater
229         // than we need now.
230         // NOTE - we deliberately allow this code to blow up if you try to pop
231         // an object off an empty stack. You're going to get an exception
232         // anyway so it might as well be an index out of bounds.
233         return this.valueStack[ --this.valueStackIndex ];
234     }
235 
236     /**
237      * Pushes all of the values in the specified array onto the stack. The
238      * items are pushed onto the stack from first in the array to the last.
239      *
240      * @param args	the array to get values to push onto the stack from
241      */
242     public void pushArgsArray( final Object[] args ) {
243         for ( int i = 0; i < args.length; i++ ) {
244             this.pushObject( args[ i ] );
245         }
246     }
247 
248     /**
249      * Pushes each of the values from the specified iterator onto the stack.
250      *
251      * @param it	the iterator to get values to push onto the stack from
252      */
253     public void pushArgsIterator( final Iterator< ? > it ) {
254         while ( it.hasNext() ) {
255             this.pushObject( it.next() );
256         }
257     }
258 
259     /**
260      * Pushes all of the values in the specified list onto the stack. The items
261      * are pushed onto the stack from first in the list to the last.
262      *
263      * @param list	the list to get values to push onto the stack from
264      */
265     public void pushArgsList( final IList< ? > list ) {
266         for ( ListIterator< ? > it = list.iterator( true ); it.hasNext(); ) {
267             this.pushObject( it.nextValue() );
268         }
269     }
270 
271     /**
272      * Pushes each of the values from the specified map iterator onto the
273      * stack. Note, this method ignores the keys from the supplied map iterator
274      * and only pushes the values.
275      *
276      * @param it    the map iterator to push values onto the stack from
277      */
278     public void pushArgsMapIterator( final MapIterator< ?, ? > it ) {
279         while ( it.hasNext() ) {
280             this.pushObject( it.nextValue() );
281         }
282     }
283 
284     /**
285      * Pushes the specified boolean value onto the operand stack.
286      *
287      * @param b	a boolean value to push onto the operand stack.
288      */
289     public void pushBoolean( final boolean b ) {
290         if ( this.valueStackIndex >= this.valueStack.length ) {
291             // We need to grow the array
292             final Object[] newArray = new Object[ (int) ( this.valueStack.length * 1.5 ) ];
293             System.arraycopy( this.valueStack, 0, newArray, 0, this.valueStack.length );
294             this.valueStack = newArray;
295         }
296         // "push" the new value on the end
297         this.valueStack[ this.valueStackIndex++ ] = b ? Boolean.TRUE : Boolean.FALSE;
298     }
299 
300     /**
301      * Pushes the specified object onto the operand stack.
302      *
303      * @param val	an object to push onto the operand stack.
304      */
305     public void pushObject( final Object val ) {
306         if ( this.valueStackIndex >= this.valueStack.length ) {
307             // We need to grow the array
308             final Object[] newArray = new Object[ (int) ( this.valueStack.length * 1.5 ) ];
309             System.arraycopy( this.valueStack, 0, newArray, 0, this.valueStack.length );
310             this.valueStack = newArray;
311         }
312         // "push" the new value on the end
313         this.valueStack[ this.valueStackIndex++ ] = val;
314     }
315 
316     /**
317      * Resets this machine after an error, making it ready to action new code.
318      */
319     public void reset() {
320         this.valueStackIndex = 0;
321         this.piggyBankIndex = 0;
322         this.doingIndex = 0;
323     }
324 
325     /**
326      * Restore all the variables in the current save set (in reverse order) and
327      * dispose of the save set.
328      */
329     public void restoreSaved() {
330         for (;;) {
331             Object v = this.piggyBank[ --this.piggyBankIndex ];
332             if ( v == STACKMARK ) {
333                 break;
334             }
335             Ref r = (Ref) this.piggyBank[ --this.piggyBankIndex ];
336             r.value = v;
337         }
338     }
339 
340     /**
341      * Save a variable and its current value.
342      *
343      * @param r	the reference to save
344      */
345     public void saveRef( final Ref r ) {
346         if ( this.piggyBankIndex >= this.piggyBank.length ) {
347             // We need to grow the array
348             final Object[] newArray = new Object[ (int) ( this.piggyBank.length * 1.5 ) ];
349             System.arraycopy( this.piggyBank, 0, newArray, 0, this.piggyBank.length );
350             this.piggyBank = newArray;
351         }
352         // "push" the new value on the end
353         this.piggyBank[ this.piggyBankIndex++ ] = r;
354         if ( this.piggyBankIndex >= this.piggyBank.length ) {
355             // We need to grow the array
356             final Object[] newArray = new Object[ (int) ( this.piggyBank.length * 1.5 ) ];
357             System.arraycopy( this.piggyBank, 0, newArray, 0, this.piggyBank.length );
358             this.piggyBank = newArray;
359         }
360         this.piggyBank[ this.piggyBankIndex++ ] = r.value;
361     }
362 
363     /**
364      * Sets the number of values on the stack. The new size should be less
365      * than the current size of the stack, i.e. this method cannot be used to
366      * increase the size of the stack.
367      *
368      * @param n	the maximum number of values that should remain on the stack
369      */
370     public void setCount( final int n ) {
371         // Stack index 0, 1, 2, 3, 4, 5, 6
372         // newcountmax 0, 1, 2, 3, 4, 5, 6
373         if ( n <= valueStackIndex ) {
374             this.valueStackIndex = n;
375         } else {
376             throw new IllegalArgumentException( "new count must be not be larger than the current count" );
377         }
378     }
379 
380     /**
381      * Start a new save-set.
382      */
383     public void startSaving() {
384         if ( this.piggyBankIndex >= this.piggyBank.length ) {
385             // We need to grow the array
386             final Object[] newArray = new Object[ (int) ( this.piggyBank.length * 1.5 ) ];
387             System.arraycopy( this.piggyBank, 0, newArray, 0, this.piggyBank.length );
388             this.piggyBank = newArray;
389         }
390         // "push" the new value on the end
391         this.piggyBank[ this.piggyBankIndex++ ] = STACKMARK;
392     }
393 
394 }