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) 2004-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.alert.Alert;
25  import org.millscript.commons.alert.EscapeException;
26  import org.millscript.commons.util.MapIterator;
27  import org.millscript.millscript.action.Action;
28  import org.millscript.millscript.alert.Alerts;
29  import org.millscript.millscript.alert.Phases;
30  import org.millscript.millscript.conf.functions.ShowExprFunction;
31  import org.millscript.millscript.expr.ApplyExpr;
32  import org.millscript.millscript.expr.CheckExpr;
33  import org.millscript.millscript.expr.ConstantExpr;
34  import org.millscript.millscript.expr.Expr;
35  import org.millscript.millscript.expr.GlobalIdent;
36  import org.millscript.millscript.expr.ImportExpr;
37  import org.millscript.millscript.expr.NameExpr;
38  import org.millscript.millscript.expr.PackageExpr;
39  import org.millscript.millscript.expr.PragmaExpr;
40  import org.millscript.millscript.expr.VarExpr;
41  import org.millscript.millscript.loaders.Loader;
42  import org.millscript.millscript.syntax.Parser;
43  import org.millscript.millscript.syntax.TokenType;
44  
45  import java.util.Stack;
46  
47  /**
48   * This class represents the compiler state during compilation from a given
49   * parser.
50   */
51  public final class CompilerState {
52  
53      /**
54       * The current scopes. This is used during the expression rename phase.
55       */
56      private Stack< Expr > scopes = new Stack< Expr >();
57  
58      /**
59       * The current package we are compiling into. This may change during
60       * compilation when package expressions are compiled.
61       */
62      private Package currentPackage;
63  
64      /**
65       * The engine that we will use for executing compiled code.
66       */
67      private Engine engine;
68  
69      /**
70       * Constructs a new <code>CompilerState</code> to compile and execute code
71       * in the specified engine.
72       *
73       * @param e the engine to compile and execute code in
74       */
75      public CompilerState( final Engine e ) {
76          this.engine = e;
77      }
78  
79      /**
80       * Changes the current package to the specified one.
81       *
82       * @param p the new current package
83       */
84      public void changePackage( final Package p ) {
85          currentPackage = p;
86      }
87  
88      /**
89       * Checks if the specified symbol is protected in the current package.
90       *
91       * @param sym   the symbol to test
92       * @return  <code>true</code> if the symbol is protected in the current
93       * package, <code>false</code> otherwise
94       */
95      public boolean getIsProtected( final String sym ) {
96          return currentPackage.getIsProtected( sym );
97      }
98  
99      /**
100      * Declares the specified symbol as a global variable in the current
101      * package.
102      *
103      * @param sym   the symbol to declare as a global
104      * @return  the GlobalIdent for the symbol
105      */
106     public GlobalIdent declareGlobal( final String sym ) {
107         return currentPackage.declareGlobal( sym );
108     }
109 
110     /**
111      * Finds the GlobalIdent for the specified symbol in the specified package.
112      *
113      * @param nickname  the nickname of the package the symbol is in
114      * @param sym   the symbol we want to find the Ident for
115      * @return  the GlobalIdent for the symbol
116      */
117     public GlobalIdent findIdent( final String nickname, final String sym ) {
118         return currentPackage.findIdentFor( nickname, sym );
119     }
120 
121     /**
122      * Returns the stack of scopes.
123      *
124      * @return  the Stack of scopes
125      */
126     public Stack< Expr > getScopes() {
127         return scopes;
128     }
129 
130     /**
131      * Declares all the expressions in scope as local variables, from the
132      * specified position in the scope stack to the top.
133      *
134      * @param prev  the position in the scope stack to start declaring as local
135      * variables from
136      */
137     public void declareLocalsFrom( final int prev ) {
138         int now = scopes.size();
139         for ( int i = prev; i < now; i++ ) {
140             NameExpr name = (NameExpr)scopes.elementAt( i );
141             name.declareLocal( true );
142         }
143     }
144 
145     /**
146      * Utility method for reporting a bad pragma alert.
147      *
148      * @param wrapped   the expression wrapped as a pragma
149      */
150     private void badPragma( final Expr wrapped ) {
151         throw(
152             Alerts.compile(
153                 "Unknown pragma",
154                 null
155             ).culprit( "pragma", wrapped ).mishap()
156         );
157     }
158 
159     /**
160      * Returns the string value of the specified expression, providing it is a
161      * constant string.
162      *
163      * @param args  the constant string expression
164      * @return  the String value of the expression
165      */
166     private String getStrArg( final Expr args ) {
167         if ( args instanceof ConstantExpr ) {
168             Object x = ((ConstantExpr)args).getValue();
169             if ( x instanceof String ) {
170                 return (String)x;
171             }
172         }
173         return null;
174     }
175 
176     /**
177      * Processes the specified pragma expression.
178      *
179      * @param wrapped   the expression wrapped as a pragma
180      */
181     private void processPragma( final Expr wrapped ) {
182         //  processPragma is deliberately written in this style of
183         //  cascading if-statements in order to accommodate future
184         //  pragmas.
185         if ( wrapped instanceof ApplyExpr ) {
186             Expr fun = ((ApplyExpr)wrapped).getFun();
187             Expr args = ((ApplyExpr)wrapped).getArgs();
188             if ( fun instanceof NameExpr ) {
189                 String sym = ((NameExpr)fun).getName();
190                 if ( sym == "loadJavaClass" ) {
191                     String x = getStrArg( args );
192                     if ( x != null ) {
193                         new ImportJavaClass( x, currentPackage ).importAll();
194                         return;
195                     }
196                 } else if ( sym == "show" ) {
197                     String x = getStrArg( args );
198                     if ( x != null ) {
199                         Package p = engine.getPackage( x );
200                         if ( p == null ) {
201                             System.out.println( "No package by this name: " + x );
202                         } else {
203                             System.out.println( "Package " + x );
204                             int n = 0;
205                             MapIterator< String, GlobalIdent > it = p.bindingsIterator();
206                             while ( it.hasNext() ) {
207                                 String name = it.nextKey();
208                                 System.out.println( "(" + n++ + ") " + name );
209                             }
210                             MapIterator< String, Loader > at = p.autoloadablesIterator();
211                             while ( at.hasNext() ) {
212                                 String name = at.nextKey();
213                                 System.out.println( "(" + n++ + ") " + name + " [autoloadable]" );
214                             }
215                         }
216                         return;
217                     }
218                 }
219             }
220         }
221         badPragma( wrapped );
222     }
223 
224     /**
225      * Compiles the specified object as a constant value and binds it to the
226      * specified symbol.
227      *
228      * @param sym   the symbol to define as a const
229      * @param obj   the value to make a constant
230      */
231     public void compileConstant( final String sym, final Object obj ) {
232         compileConst( sym, new ConstantExpr( obj ) );
233     }
234 
235     /**
236      * Compiles the specified expression as the value of a constant variable
237      * with the specified symbol.
238      *
239      * @param sym   the symbol to define as a const
240      * @param e the expression for the value of the const
241      */
242     public void compileConst( final String sym, final Expr e ) {
243         compileExpr(
244             new VarExpr(
245                 true,
246                 new NameExpr( sym ),
247                 e
248             )
249         );
250     }
251 
252     /**
253      * Compiles the specified expression and executes it in the engine
254      * associated with this compiler state.
255      *
256      * @param e the expression to compile and execute
257      */
258     @SuppressWarnings( "unchecked" )
259     public void compileExpr( final Expr e ) {
260         // FIXME - When Sun fix their java compiler the method parameter can be
261         // made Expr< ? > and the suppress warnings can be removed
262         if ( e instanceof PackageExpr ) {
263             PackageExpr pe = (PackageExpr) e;
264             changePackage( engine.fetchPackage( pe.getFullName() ) );
265         } else if ( e instanceof ImportExpr ) {
266             ImportExpr ie = (ImportExpr) e;
267             Package p = engine.getPackage( ie.getFullName() );
268             if ( p == null ) {
269                 throw(
270                     Alerts.compile(
271                         "Cannot import this package",
272                         "The named package cannot be loaded"
273                     ).culprit( "package name", ie.getFullName() ).mishap()
274                 );
275             }
276             currentPackage.importPackage( ie.getNickname(), p );
277         } else if ( e instanceof PragmaExpr ) {
278             processPragma( ((PragmaExpr) e).getPragmaExpr() );
279         } else {
280             try {
281                 e.resolve( this );
282             } catch ( Alert a ) {
283                 a.setPhase( Phases.RENAME ).remishap();
284             }
285             e.compile().act( engine.getMachine() );
286         }
287     }
288 
289     /**
290      * Compiles the specified expression to it's action tree.
291      *
292      * @param e the expression to compile
293      * @return  the action tree for the specified expression
294      */
295     public Action compileExprToAction( Expr< ? > e ) {
296         try {
297             e.resolve( this );
298         } catch ( Alert a ) {
299             a.setPhase( Phases.RENAME ).remishap();
300         }
301         return e.compile();
302     }
303 
304     /**
305      * Compiles the specified expression, executes it in the engine
306      * associated with this compiler state and returns the last result the
307      * expression generates.
308      *
309      * @param e the expression to compile and execute
310      * @return  the last result returned after compiling and executing the
311      * specified expression
312      */
313     public Object compileExprToValue( Expr< ? > e ) {
314         e = CheckExpr.make( e );
315         try {
316             e.resolve( this );
317         } catch ( Alert a ) {
318             a.setPhase( Phases.RENAME ).remishap();
319         }
320         return e.compile().act1( engine.getMachine() );
321     }
322 
323     /**
324      * Compiles the next expression from the supplied parser.
325      *
326      * @param parser    the parser to compile an expression from.
327      */
328     public void compile( final Parser parser ) {
329         Expr< ? > expr = parser.readExprTo( ";" );
330         if ( this.engine.getConfig().getBooleanProperty( ShowExprFunction.KEY ) ) {
331             expr.show();
332         }
333         compileExpr( expr );
334     }
335 
336     /**
337      * Compiles expressions from the supplied parser, ensuring no results are
338      * returned after compiling each expression.
339      *
340      * @param parser    the Parser to read expressions from
341      */
342     public void compileNoResults( final Parser parser ) {
343         try {
344             while ( parser.peekToken() != TokenType.EOF ) {
345                 this.compile( parser );
346                 if ( engine.getMachine().getCount() > 0 ) {
347                     throw(
348                         Alerts.eval(
349                             "Results returned but not at top level",
350                             "Results can only be returned at top level"
351                         ).origin( parser ).mishap()
352                     );
353                 }
354             }
355         } catch ( Alert ex ) {
356             // Escape from here. The alert will be reported at the top
357             // level(hopefully). Absolutely, finally, try to decorate the
358             // alert, if it isn't already. This is required to catch problems
359             // with the parser.peekToken() line above.
360             // Oh, and report it too!
361             throw ex.origin( parser ).setPhase( Phases.COMPILE ).escape();
362         } catch ( EscapeException ex ) {
363             // Just throw it again to escape outta' here
364             throw ex;
365         } catch ( Exception ex ) {
366             // Oh dear. It's not an alert, we better give some indication of
367             // what happened...
368             this.engine.getConfig().reportException( ex );
369             // and now escape outta' this mess
370             throw new EscapeException( null );
371         }
372     }
373 
374     /**
375      * Compiles expressions from the supplied parser, allowing results to be
376      * returned by each compiled each expression.
377      *
378      * @param parser    the Parser to read expressions from
379      * @param interactive   indicates if this compilation is being done from an
380      * interactive mode
381      */
382     public void compileAllowResults( final Parser parser, final boolean interactive ) {
383         try {
384             while ( parser.peekToken() != TokenType.EOF ) {
385                 this.compile( parser );
386                 engine.getMachine().printResults( interactive );
387             }
388         } catch ( Alert ex ) {
389             // Escape from here. The alert will be reported at the top
390             // level(hopefully). Absolutely, finally, try to decorate the
391             // alert, if it isn't already. This is required to catch problems
392             // with the parser.peekToken() line above.
393             // Oh, and report it too!
394             throw ex.origin( parser ).escape();
395         } catch ( EscapeException ex ) {
396             // Just throw it again to escape outta' here
397             throw ex;
398         } catch ( Exception ex ) {
399             // Oh dear. It's not an alert, we better give some indication of
400             // what happened...
401             this.engine.getConfig().reportException( ex );
402             // and now escape outta' this mess
403             throw new EscapeException( null );
404         }
405     }
406 
407 }