1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
183
184
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
261
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
357
358
359
360
361 throw ex.origin( parser ).setPhase( Phases.COMPILE ).escape();
362 } catch ( EscapeException ex ) {
363
364 throw ex;
365 } catch ( Exception ex ) {
366
367
368 this.engine.getConfig().reportException( ex );
369
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
390
391
392
393
394 throw ex.origin( parser ).escape();
395 } catch ( EscapeException ex ) {
396
397 throw ex;
398 } catch ( Exception ex ) {
399
400
401 this.engine.getConfig().reportException( ex );
402
403 throw new EscapeException( null );
404 }
405 }
406
407 }