View Javadoc

1   ////////////////////////////////////////////////////////////////////////////////
2   // MillScript: an Open Spice interpreter and batch website creation tool
3   // Copyright (C) 2004 Kevin Rogers
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.vm;
22  
23  import org.millscript.commons.util.ListIterator;
24  import org.millscript.commons.util.list.ELinkedList;
25  import org.millscript.commons.util.map.EHashMap;
26  import org.millscript.commons.vfs.VFile;
27  import org.millscript.commons.vfs.VFolder;
28  import org.millscript.millscript.conf.Configuration;
29  import org.millscript.millscript.functions.Function;
30  import org.millscript.millscript.loaders.Loader;
31  
32  /**
33   * This class represents a MillScript engine.
34   */
35  public class Engine {
36  
37      /**
38       * Maps available package fully qualified names to their package objects.
39       * This is just a store to avoid re-loading the same package again and
40       * again.
41       */
42      private final EHashMap< String, Package > availablePackages;
43  
44      /**
45       * This engines configuration.
46       */
47      private final Configuration config;
48  
49      /**
50       * List of directories available for loading packages from disk.
51       */
52      private final ELinkedList< VFolder > inventoryList;
53  
54      /**
55       * The machine this engine will use to execute code in.
56       */
57      private Machine machine;
58  
59      /**
60       * Constructs a new engine with a default package.
61       *
62       * @param conf  the configuration for this engine
63       */
64      public Engine( final Configuration conf ) {
65          // Copy the configuration
66          this.config = conf;
67          // Initialise the available package store and inventory
68          // list
69          this.availablePackages = new EHashMap< String, Package >();
70          this.inventoryList = new ELinkedList< VFolder >();
71          // Initialise this engines machine
72          this.machine = new Machine( conf );
73      }
74  
75      /**
76       * Adds the specified location as an inventory.
77       *
78       * @param f the location of an inventory for this engine.
79       */
80      public void addInventory( final VFolder f ) {
81          if ( f != null ) {
82              this.inventoryList.addLast( f );
83          }
84      }
85  
86      /**
87       * Adds the specified loader for the specified file extension.
88       *
89       * @param extension the file extension this loader handles.
90       * @param loader    the loader for the specified file extension.
91       */
92      public void addLoader( final String extension, final Loader loader ) {
93          this.config.getLoaderBuilder().addLoader( extension, loader );
94      }
95  
96      /**
97       * Adds the specified package to this engine.
98       *
99       * @param p the package to add to this engine.
100      */
101     public void addPackage( final Package p ) {
102         this.availablePackages.insert( p.getFullyQualifiedPackageName(), p );
103     }
104 
105     /**
106      * Calls the specified function with the two specified arguments,
107      * returning no results.
108      *
109      * @param f the function to call
110      * @param x the functions first argument
111      * @param y the functions second argument
112      */
113     public void callBinary0( final Function f, final Object x, final Object y ) {
114         final FunctionCall fc = this.newFunctionCall();
115         fc.setFunction( f );
116         fc.addArg( x );
117         fc.addArg( y );
118         fc.invoke0();
119     }
120 
121     /**
122      * Calls the specified function with the two specified arguments,
123      * returning a single result.
124      *
125      * @param f the function to call
126      * @param x the functions first argument
127      * @param y the functions second argument
128      * @return  the functions single result
129      */
130     public Object callBinary1( final Function f, final Object x, final Object y ) {
131         final FunctionCall fc = this.newFunctionCall();
132         fc.setFunction( f );
133         fc.addArg( x );
134         fc.addArg( y );
135         return fc.invoke1();
136     }
137 
138     /**
139      * Calls the specified function with no arguments, returning no results.
140      *
141      * @param f the function to call
142      */
143     public void callNullary0( final Function f ) {
144         final FunctionCall fc = this.newFunctionCall();
145         fc.setFunction( f );
146         fc.invoke0();
147     }
148 
149     /**
150      * Calls the specified function with no arguments, returning a single result.
151      *
152      * @param f the function to call
153      * @return  the functions single result
154      */
155     public Object callNullary1( final Function f ) {
156         final FunctionCall fc = this.newFunctionCall();
157         fc.setFunction( f );
158         return fc.invoke1();
159     }
160 
161     /**
162      * Calls the specified function with the three specified arguments,
163      * returning no results.
164      *
165      * @param f the function to call
166      * @param x the functions first argument
167      * @param y the functions second argument
168      * @param z the functions third argument
169      */
170     public void callTrinary0( final Function f, final Object x, final Object y, final Object z ) {
171         final FunctionCall fc = this.newFunctionCall();
172         fc.setFunction( f );
173         fc.addArg( x );
174         fc.addArg( y );
175         fc.addArg( z );
176         fc.invoke0();
177     }
178 
179     /**
180      * Calls the specified function with the three specified arguments,
181      * returning a single result.
182      *
183      * @param f the function to call
184      * @param x the functions first argument
185      * @param y the functions second argument
186      * @param z the functions third argument
187      * @return  the functions single result
188      */
189     public Object callTrinary1( final Function f, final Object x, final Object y, final Object z ) {
190         final FunctionCall fc = this.newFunctionCall();
191         fc.setFunction( f );
192         fc.addArg( x );
193         fc.addArg( y );
194         fc.addArg( z );
195         return fc.invoke1();
196     }
197 
198     /**
199      * Calls the specified function with the single specified argument,
200      * returning no results.
201      *
202      * @param f the function to call
203      * @param x the functions single argument
204      */
205     public void callUnary0( final Function f, final Object x ) {
206         final FunctionCall fc = this.newFunctionCall();
207         fc.setFunction( f );
208         fc.addArg( x );
209         fc.invoke0();
210     }
211 
212     /**
213      * Calls the specified function with the single specified argument,
214      * returning a single result.
215      *
216      * @param f the function to call
217      * @param x the functions single argument
218      * @return  the functions single result
219      */
220     public Object callUnary1( final Function f, final Object x ) {
221         final FunctionCall fc = this.newFunctionCall();
222         fc.setFunction( f );
223         fc.addArg( x );
224         return fc.invoke1();
225     }
226 
227     /**
228      * Returns the requested package, loading it from an inventory if it hasn't
229      * been already. If the requested package cannot be found, a new one will
230      * be created.
231      *
232      * @param fqn   the fully qualified name of the package to fetch
233      * @return  the requested package
234      */
235     public Package fetchPackage( final String fqn ) {
236         final Package p = this.getPackage( fqn );
237         if ( p != null ) {
238             return p;
239         } else {
240             return this.newPackage( fqn );
241         }
242     }
243 
244     /**
245      * Returns this engines configuration.
246      *
247      * @return  this engines configuration
248      */
249     public Configuration getConfig() {
250         return config;
251     }
252 
253     /**
254      * Returns this engines machine, to execute code on.
255      *
256      * @return  the machine for this engine
257      */
258     public Machine getMachine() {
259         return machine;
260     }
261 
262     /**
263      * Gets the requested package. The package is specified by it's fully
264      * qualified and nick-name. If the package has already been loaded from an
265      * available location, the same object will be returned, otherwise the
266      * package will be loaded automatically.
267      *
268      * @param fqn   the fully qualified name for the package.
269      * @return  the package object for the requested package.
270      */
271     public Package getPackage( final String fqn ) {
272         if ( this.availablePackages.containsKey( fqn ) ) {
273             return this.availablePackages.get( fqn );
274         } else {
275             return this.loadPackage( fqn );
276         }
277     }
278 
279     /**
280      * Loads the requested package from any of the available locations. The
281      * package is specified by the fully qualified name and nickname.
282      *
283      * @param fqn   the fully qualified name for the package.
284      * @return  the package object for the requested package.
285      * @todo    we might want to remove the add inventory stage, to assist
286      * supporting packages contain in zips, etc.
287      */
288     private Package loadPackage( final String fqn ) {
289         //    Substitute _ for .
290         final String base = fqn.replace( '.', '_' );
291 
292         final ListIterator< VFolder > it = inventoryList.iterator( true );
293         while ( it.hasNext() ) {
294             final VFolder dir = it.nextValue();
295             final VFolder pkg = dir.getVFolder( base + ".pkg" );
296             if ( pkg.exists() ) {
297                 final Package p = this.newPackage( fqn );
298                 // This code is ok in "diskless" environments as we would not
299                 // add anything to inventory_list, hence we never get here.
300                 // TODO - We probably want a VFS at this point really...
301                 p.addInventory( pkg.getVFolder( Configuration.INVENTORY_NAME ) );
302                 // Load the package configuration file, if it exists
303                 p.loadConf( this.config.getPackageConfFile( pkg ) );
304                 // Interpret the package loader
305                 final VFile packageLoader = pkg.checkVFile( base + ".ms" );
306                 p.interpret( packageLoader );
307                 return p;
308             }
309         }
310         return null;
311     }
312 
313     /**
314      * Returns a new function call object for this engine.
315      *
316      * @return  a new <code>FunctionCall</code> object for this engine.
317      */
318     public FunctionCall newFunctionCall() {
319         return new FunctionCall( this );
320     }
321 
322     /**
323      * Returns a new function call object, to invoke the specified functoin on
324      * this engine.
325      *
326      * @param f the function we want to invoke.
327      * @return  a <code>FunctionCall</code> object to invoke the specified
328      * function on this machine.
329      */
330     public FunctionCall newFunctionCall( final Function f ) {
331         return new FunctionCall( this, f );
332     }
333 
334     /**
335      * Returns a new function call object, to invoke the specified functoin on
336      * this engine.
337      *
338      * @param p the package which contains the function.
339      * @param sym   the symbol of the function to invoke.
340      * @return  a <code>FunctionCall</code> object to invoke the specified
341      * function on this machine.
342      */
343     public FunctionCall newFunctionCall( final Package p, final String sym ) {
344         return new FunctionCall( this, p, sym );
345     }
346 
347     /**
348      * Returns a package for the specified fully qualified name and nick-name.
349      * If a package already exists with the specified name, that package will be
350      * returned, otherwise a new empty package is created.
351      *
352      * @param fqn   the fully qualified name for the new package.
353      * @return  a new package, or the existing one, for the specified name.
354      */
355     public Package newPackage( final String fqn ) {
356         // TODO - must add inventory to package, otherwise some code will not work.
357         // Which code, dunce? I really should have put a better comment here.
358         final Package p = new Package( this, fqn, false );
359         this.addPackage( p );
360         return p;
361     }
362 
363 }