]> gerrit.simantics Code Review - simantics/platform.git/blob - ExpressionEvaluator.java
6cb68b67364046470a52ebcc7b3c6486bfaf9480
[simantics/platform.git] / ExpressionEvaluator.java
1 package org.simantics.scl.compiler.top;
2
3 import gnu.trove.set.hash.THashSet;
4
5 import java.io.StringReader;
6 import java.lang.reflect.Method;
7 import java.util.ArrayList;
8 import java.util.ListIterator;
9 import java.util.Map;
10
11 import org.simantics.scl.compiler.common.names.Name;
12 import org.simantics.scl.compiler.compilation.CodeGeneration;
13 import org.simantics.scl.compiler.constants.JavaStaticMethod;
14 import org.simantics.scl.compiler.constants.SCLConstant;
15 import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext;
16 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
17 import org.simantics.scl.compiler.elaboration.contexts.TypingContext;
18 import org.simantics.scl.compiler.elaboration.errors.NotPatternException;
19 import org.simantics.scl.compiler.elaboration.expressions.EApply;
20 import org.simantics.scl.compiler.elaboration.expressions.EBlock;
21 import org.simantics.scl.compiler.elaboration.expressions.EConstant;
22 import org.simantics.scl.compiler.elaboration.expressions.EExternalConstant;
23 import org.simantics.scl.compiler.elaboration.expressions.EVar;
24 import org.simantics.scl.compiler.elaboration.expressions.Expression;
25 import org.simantics.scl.compiler.elaboration.expressions.block.GuardStatement;
26 import org.simantics.scl.compiler.elaboration.expressions.block.LetStatement;
27 import org.simantics.scl.compiler.elaboration.expressions.block.Statement;
28 import org.simantics.scl.compiler.elaboration.java.Builtins;
29 import org.simantics.scl.compiler.environment.Environment;
30 import org.simantics.scl.compiler.environment.LocalEnvironment;
31 import org.simantics.scl.compiler.errors.ErrorLog;
32 import org.simantics.scl.compiler.internal.codegen.references.IVal;
33 import org.simantics.scl.compiler.internal.codegen.ssa.SSAModule;
34 import org.simantics.scl.compiler.internal.codegen.types.DummyJavaReferenceValidator;
35 import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator;
36 import org.simantics.scl.compiler.internal.codegen.utils.CodeBuildingException;
37 import org.simantics.scl.compiler.internal.codegen.utils.JavaNamingPolicy;
38 import org.simantics.scl.compiler.internal.codegen.utils.ModuleBuilder;
39 import org.simantics.scl.compiler.internal.codegen.utils.TransientClassBuilder;
40 import org.simantics.scl.compiler.internal.codegen.utils.ValueFromMethod;
41 import org.simantics.scl.compiler.internal.codegen.writer.CodeWriter;
42 import org.simantics.scl.compiler.internal.codegen.writer.ExternalConstant;
43 import org.simantics.scl.compiler.internal.codegen.writer.ModuleWriter;
44 import org.simantics.scl.compiler.internal.elaboration.decomposed.DecomposedExpression;
45 import org.simantics.scl.compiler.internal.elaboration.utils.ExpressionDecorator;
46 import org.simantics.scl.compiler.internal.interpreted.IExpression;
47 import org.simantics.scl.compiler.internal.parsing.exceptions.SCLSyntaxErrorException;
48 import org.simantics.scl.compiler.internal.parsing.parser.SCLBlockParser;
49 import org.simantics.scl.compiler.internal.parsing.parser.SCLParserImpl;
50 import org.simantics.scl.compiler.internal.parsing.parser.SCLParserOptions;
51 import org.simantics.scl.compiler.runtime.MutableClassLoader;
52 import org.simantics.scl.compiler.runtime.RuntimeEnvironment;
53 import org.simantics.scl.compiler.types.TVar;
54 import org.simantics.scl.compiler.types.Type;
55 import org.simantics.scl.compiler.types.Types;
56 import org.simantics.scl.compiler.types.kinds.Kinds;
57 import org.simantics.scl.compiler.types.util.Polarity;
58 import org.simantics.scl.compiler.types.util.ProcedureType;
59 import org.simantics.scl.runtime.function.FunctionImpl1;
60 import org.simantics.scl.runtime.tuple.Tuple0;
61
62 public class ExpressionEvaluator {
63
64     public static final boolean TRACE_INTERPRETATION_VS_COMPILATION = false;
65     private static final String COMPUTATION_METHOD_NAME = "main";
66     
67     private final RuntimeEnvironment runtimeEnvironment;
68     private final String expressionText;
69     private Expression expression;
70     private Type expressionType;
71     
72     private Type expectedEffect;
73     private boolean decorateExpression;
74     private Type expectedType;
75     private LocalEnvironment localEnvironment;
76     private LocalStorage localStorage;
77     private boolean interpretIfPossible = true;
78     private ExpressionParseMode parseMode = ExpressionParseMode.EXPRESSION;
79     
80     public ExpressionEvaluator(RuntimeEnvironment runtimeEnvironment,
81             String expressionText) {
82         if(runtimeEnvironment == null)
83             throw new NullPointerException();
84         if(expressionText == null)
85             throw new NullPointerException();
86         this.runtimeEnvironment = runtimeEnvironment;
87         this.expressionText = expressionText;
88     }
89     
90     public ExpressionEvaluator(RuntimeEnvironment runtimeEnvironment,
91             LocalStorage localStorage, Expression expression) {
92         if(runtimeEnvironment == null)
93             throw new NullPointerException();
94         if(expression == null)
95             throw new NullPointerException();
96         this.runtimeEnvironment = runtimeEnvironment;
97         this.localStorage = localStorage;
98         this.expressionText = null;
99         this.expression = expression;
100     }
101     
102     public ExpressionEvaluator expectedEffect(Type expectedEffect) {
103         this.expectedEffect = expectedEffect;
104         return this;
105     }
106     
107     public ExpressionEvaluator decorateExpression(boolean decorateExpression) {
108         this.decorateExpression = decorateExpression;
109         return this;
110     }
111     
112     public ExpressionEvaluator expectedType(Type expectedType) {
113         this.expectedType = expectedType;
114         return this;
115     }
116     
117     /**
118      * Sets a local environment that can arbitrarily modify the resolving of the expression.
119      */
120     public ExpressionEvaluator localEnvironment(LocalEnvironment localEnvironment) {
121         this.localEnvironment = localEnvironment;
122         return this;
123     }
124     
125     /**
126      * Evaluates the expression by interpretation instead of compilation to bytecode
127      * if the expression does not contain language constructs that interpretation does
128      * not support.
129      */
130     public ExpressionEvaluator interpretIfPossible(boolean interpretIfPossible) {
131         this.interpretIfPossible = interpretIfPossible;
132         return this;
133     }
134     
135     /**
136      * Assumes that top level of the expression is similar to the content
137      * of a do-block.
138      */
139     public ExpressionEvaluator parseAsBlock(boolean parseAsBlock) {
140         this.parseMode = parseAsBlock ? ExpressionParseMode.BLOCK : ExpressionParseMode.EXPRESSION;
141         return this;
142     }
143     
144     public ExpressionEvaluator parseModel(ExpressionParseMode parseMode) {
145         this.parseMode = parseMode;
146         return this;
147     }
148     
149     private void fillDefaults() {
150         if(expectedEffect == null)
151             expectedEffect = Types.metaVar(Kinds.EFFECT);
152         if(expectedType == null)
153             expectedType = Types.metaVar(Kinds.STAR);
154     }
155     
156     private static class StoreFunction extends FunctionImpl1<Object, Object> {
157         final LocalStorage storage;
158         final String name;
159         final Type type;
160         public StoreFunction(LocalStorage storage, String name, Type type) {
161             this.storage = storage;
162             this.name = name;
163             this.type = type;
164         }
165         @Override
166         public Object apply(Object value) {
167             Type type = Types.closure(this.type.convertMetaVarsToVars());
168             storage.store(name, value, type);
169             return Tuple0.INSTANCE;
170         }
171         
172         @Override
173         public String toString() {
174             return "store_" + name;
175         }
176     }
177
178     public Object eval() throws SCLExpressionCompilationException {
179         fillDefaults();
180         
181         final ErrorLog errorLog = new ErrorLog();
182         final Environment environment = runtimeEnvironment.getEnvironment();
183         
184         // Parse expression
185         if(expressionText != null) {
186             try {
187                 switch(parseMode) {
188                 case BLOCK: {
189                     SCLBlockParser parser = new SCLBlockParser(new StringReader(expressionText));
190                     parser.parseCommands();
191                     expression = parser.block;
192                 } break;
193                 case EXPRESSION: {
194                     SCLParserImpl parser = new SCLParserImpl(new StringReader(expressionText));
195                     expression = (Expression)parser.parseExp();
196                 } break;
197                 case EQUATION_BLOCK: {
198                     SCLParserImpl parser = new SCLParserImpl(new StringReader(expressionText));
199                     SCLParserOptions parserOptions = new SCLParserOptions();
200                     parserOptions.supportEq = true;
201                     parser.setParserOptions(parserOptions);
202                     expression = (Expression)parser.parseEquationBlock();
203                 } break;
204                 }
205             } catch(SCLSyntaxErrorException e) {
206                 errorLog.log(e.location, e.getMessage());
207                 //System.out.println(errorLog.getErrorsAsString());
208                 throw new SCLExpressionCompilationException(errorLog.getErrors());
209             } catch(Exception e) {
210                 errorLog.log(e);
211                 throw new SCLExpressionCompilationException(errorLog.getErrors());
212             }
213         }
214         
215         // Store local variables
216         ArrayList<Type> lvTypes = new ArrayList<Type>(); 
217         if(expression instanceof EBlock) {
218             EBlock block = (EBlock)expression;
219             if(localStorage != null && !(block.getStatements().getLast() instanceof GuardStatement)) {
220                 THashSet<String> localVariables = new THashSet<String>();
221                 ListIterator<Statement> it = block.getStatements().listIterator();
222                 while(it.hasNext()) {
223                     Statement stat = it.next();
224                     if(!(stat instanceof LetStatement))
225                         continue;
226                     String variableName;
227                     try {
228                         variableName = ((LetStatement)stat).pattern.getPatternHead().name;
229                     } catch (NotPatternException e) {
230                         continue;
231                     }
232                     localVariables.add(variableName);
233                 }
234                 for(String variableName : localVariables) {
235                     Type type = Types.metaVar(Kinds.STAR);
236                     lvTypes.add(type);
237                     block.addStatement(new GuardStatement(new EApply(
238                             new EExternalConstant(
239                                     new StoreFunction(localStorage, variableName, type),
240                                     Types.functionE(type, Types.PROC, Types.UNIT)),
241                                     new EVar(variableName)
242                             )));
243                 }
244             }
245             if(!(block.getStatements().getLast() instanceof GuardStatement))
246                 block.addStatement(new GuardStatement(new EConstant(Builtins.TUPLE_CONSTRUCTORS[0])));
247         }
248         
249         // Elaboration
250         {
251             TranslationContext context = new TranslationContext(errorLog,
252                     environment, localEnvironment);
253             expression = expression.resolve(context);
254             if(!errorLog.isEmpty())
255                 throw new SCLExpressionCompilationException(errorLog.getErrors());
256         }
257         
258         // Apply local environment
259         if(localEnvironment != null) {
260             expression = localEnvironment.preDecorateExpression(expression);
261             ProcedureType procedureType = localEnvironment.decorateExpectedType(expectedType, expectedEffect);
262             expectedType = procedureType.type;
263             expectedEffect = procedureType.effect;
264         }
265         
266         // Type checking
267         {
268             TypingContext context = new TypingContext(errorLog, environment);
269
270             context.pushEffectUpperBound(expression.location, expectedEffect);
271             expression = expression.checkType(context, expectedType);
272             context.popEffectUpperBound();
273
274             for(Type lvType : lvTypes)
275                 lvType.addPolarity(Polarity.POSITIVE);
276             
277             expectedType.addPolarity(Polarity.POSITIVE);
278             context.solveSubsumptions(expression.location);
279             if(!errorLog.isEmpty())
280                 throw new SCLExpressionCompilationException(errorLog.getErrors());
281             if(decorateExpression && Types.canonical(expectedEffect) != Types.NO_EFFECTS) {
282                 ExpressionDecorator decorator =
283                         new ToplevelEffectDecorator(errorLog, environment);
284                 expression = expression.decorate(decorator);
285             }
286             expression = context.solveConstraints(environment, expression);
287             expressionType = expression.getType();
288             
289             if(!errorLog.isEmpty())
290                 throw new SCLExpressionCompilationException(errorLog.getErrors());
291
292             if(localEnvironment != null)
293                 expression = localEnvironment.postDecorateExpression(expression);
294
295             Type type = expression.getType();
296             type = type.convertMetaVarsToVars();
297             
298             for(Type lvType : lvTypes)
299                 lvType.convertMetaVarsToVars();
300             
301             ArrayList<TVar> varsList = Types.freeVars(type);
302             expression = expression.closure(varsList.toArray(new TVar[varsList.size()]));
303         }
304         
305         // Initialize code generation
306         MutableClassLoader classLoader = runtimeEnvironment.getMutableClassLoader();
307         String moduleName = classLoader.getFreshPackageName();
308         JavaTypeTranslator javaTypeTranslator = new JavaTypeTranslator(environment);
309         JavaNamingPolicy namingPolicy = new JavaNamingPolicy(moduleName);
310
311         ModuleBuilder moduleBuilder = new ModuleBuilder(namingPolicy, javaTypeTranslator);
312         
313         // Simplify
314         SimplificationContext context = 
315                 new SimplificationContext(environment, errorLog, 
316                         javaTypeTranslator, DummyJavaReferenceValidator.INSTANCE);
317         expression = expression.simplify(context);
318         
319         if(!errorLog.isEmpty())
320             throw new SCLExpressionCompilationException(errorLog.getErrors());
321         
322         if(SCLCompilerConfiguration.SHOW_EXPRESSION_BEFORE_EVALUATION)
323             System.out.println(expression);
324         
325         if(interpretIfPossible) {
326         // Try to interpret
327         try {
328             ExpressionInterpretationContext expressionInterpretationContext =
329                     new ExpressionInterpretationContext(runtimeEnvironment, 
330                             new TransientClassBuilder(classLoader, javaTypeTranslator));
331             IExpression iexp = expression.toIExpression(expressionInterpretationContext);
332                 if(TRACE_INTERPRETATION_VS_COMPILATION)
333                 System.out.println("INTERPRETED " + expressionText);
334                 if(SCLCompilerConfiguration.SHOW_INTERPRETED_EXPRESSION)
335                     System.out.println("INTERPRETED AS: " + iexp);
336             return iexp.execute(new Object[expressionInterpretationContext.getMaxVariableId()]);
337         } catch(UnsupportedOperationException e) {
338             // This is normal when expression cannot be interpreted. We compile it instead.
339         }
340         }
341         
342         // Convert to SSA
343         ModuleWriter mw = new ModuleWriter(namingPolicy.getModuleClassName());
344         DecomposedExpression decomposed = 
345                 DecomposedExpression.decompose(expression);
346
347         SCLConstant constant = new SCLConstant(
348                 Name.create(moduleName, COMPUTATION_METHOD_NAME),
349                 expression.getType());
350         constant.setBase(new JavaStaticMethod(
351                 moduleName, COMPUTATION_METHOD_NAME,
352                 decomposed.effect,
353                 decomposed.typeParameters,
354                 decomposed.returnType, 
355                 decomposed.parameterTypes));
356         try {
357             CodeWriter w = mw.createFunction(constant,
358                     decomposed.typeParameters,
359                     decomposed.effect,
360                     decomposed.returnType, 
361                     decomposed.parameterTypes);
362             constant.setDefinition(w.getFunction());
363             IVal[] parameterVals = w.getParameters();
364             for(int i=0;i<decomposed.parameters.length;++i)
365                 decomposed.parameters[i].setVal(parameterVals[i]);
366             w.return_(decomposed.body.toVal(environment, w));
367         } catch(RuntimeException e) {
368             errorLog.setExceptionPosition(expression.location);
369             throw new SCLExpressionCompilationException(errorLog.getErrors());
370         }
371
372         SSAModule ssaModule = mw.getModule();
373         if(SCLCompilerConfiguration.SHOW_SSA_BEFORE_OPTIMIZATION) {
374             System.out.println("=== SSA before optimization ==================================");
375             System.out.println(ssaModule);            
376         }
377         if(SCLCompilerConfiguration.DEBUG)
378             ssaModule.validate();
379
380         ExternalConstant[] externalConstants = mw.getExternalConstants();
381         
382         // Optimize SSA
383         for(int phase=0;phase<CodeGeneration.OPTIMIZATION_PHASES;++phase) {
384             int optCount = 0;
385             while(optCount++ < 4 && ssaModule.simplify(environment, phase)) {
386                 //System.out.println("simplify " + optCount);
387             }
388         }
389         if(SCLCompilerConfiguration.SHOW_SSA_BEFORE_LAMBDA_LIFTING) {
390             System.out.println("=== SSA before lambda lifting ==================================");
391             System.out.println(ssaModule);            
392         }
393         //ssaModule.saveInlinableDefinitions();
394         ssaModule.lambdaLift(errorLog);
395         //ssaModule.validate();
396         ssaModule.markGenerateOnFly();
397         
398         // Generate code
399         if(SCLCompilerConfiguration.SHOW_FINAL_SSA)
400             System.out.println(ssaModule);
401         try {
402             ssaModule.generateCode(moduleBuilder);
403         } catch (CodeBuildingException e) {
404             errorLog.log(e);
405             throw new SCLExpressionCompilationException(errorLog.getErrors());
406         }
407         Map<String, byte[]> classes = moduleBuilder.getClasses();
408         
409         // Load generated code and execute
410         try {
411             classLoader.addClasses(classes);
412             Class<?> clazz = classLoader.loadClass(MutableClassLoader.SCL_PACKAGE_PREFIX + moduleName);
413             for(ExternalConstant externalConstant : externalConstants)
414                 clazz.getField(externalConstant.fieldName).set(null, externalConstant.value);
415             for(Method method : clazz.getMethods()) {
416                 if(method.getName().equals(COMPUTATION_METHOD_NAME))
417                     return ValueFromMethod.getValueFromStaticMethod(method);
418             }
419             errorLog.log("Internal compiler error: didn't find method " +
420                     COMPUTATION_METHOD_NAME + " from generated byte code.");
421             throw new SCLExpressionCompilationException(errorLog.getErrors());
422         } catch(ReflectiveOperationException e) {
423             errorLog.log(e);
424             throw new SCLExpressionCompilationException(errorLog.getErrors());
425         }
426     }
427
428     public Type getType() {
429         return expressionType;
430     }
431
432     public String getExpressionText() {
433         return expressionText;
434     }
435     
436 }