1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
66 this.config = conf;
67
68
69 this.availablePackages = new EHashMap< String, Package >();
70 this.inventoryList = new ELinkedList< VFolder >();
71
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
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
299
300
301 p.addInventory( pkg.getVFolder( Configuration.INVENTORY_NAME ) );
302
303 p.loadConf( this.config.getPackageConfFile( pkg ) );
304
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
357
358 final Package p = new Package( this, fqn, false );
359 this.addPackage( p );
360 return p;
361 }
362
363 }