1 package org.simantics.scl.compiler.internal.elaboration.transformations;
3 import gnu.trove.map.hash.THashMap;
4 import gnu.trove.map.hash.TIntObjectHashMap;
5 import gnu.trove.map.hash.TObjectIntHashMap;
6 import gnu.trove.set.hash.THashSet;
8 import java.util.ArrayList;
9 import java.util.Arrays;
10 import java.util.List;
12 import org.simantics.scl.compiler.common.names.Name;
13 import org.simantics.scl.compiler.constants.Constant;
14 import org.simantics.scl.compiler.elaboration.contexts.EnvironmentalContext;
15 import org.simantics.scl.compiler.elaboration.contexts.TypingContext;
16 import org.simantics.scl.compiler.elaboration.expressions.EApply;
17 import org.simantics.scl.compiler.elaboration.expressions.EConstant;
18 import org.simantics.scl.compiler.elaboration.expressions.EEnforce;
19 import org.simantics.scl.compiler.elaboration.expressions.ERuleset;
20 import org.simantics.scl.compiler.elaboration.expressions.EVariable;
21 import org.simantics.scl.compiler.elaboration.expressions.EWhen;
22 import org.simantics.scl.compiler.elaboration.expressions.Expression;
23 import org.simantics.scl.compiler.elaboration.expressions.Expressions;
24 import org.simantics.scl.compiler.elaboration.expressions.Variable;
25 import org.simantics.scl.compiler.elaboration.expressions.VariableProcedure;
26 import org.simantics.scl.compiler.elaboration.expressions.block.GuardStatement;
27 import org.simantics.scl.compiler.elaboration.expressions.block.LetStatement;
28 import org.simantics.scl.compiler.elaboration.expressions.block.Statement;
29 import org.simantics.scl.compiler.elaboration.query.QAtom;
30 import org.simantics.scl.compiler.elaboration.query.QConjunction;
31 import org.simantics.scl.compiler.elaboration.query.QMapping;
32 import org.simantics.scl.compiler.elaboration.query.Query;
33 import org.simantics.scl.compiler.elaboration.relations.LocalRelation;
34 import org.simantics.scl.compiler.elaboration.rules.MappingRelation;
35 import org.simantics.scl.compiler.elaboration.rules.TransformationRule;
36 import org.simantics.scl.compiler.errors.ErrorLog;
37 import org.simantics.scl.compiler.errors.Locations;
38 import org.simantics.scl.compiler.internal.codegen.references.IVal;
39 import org.simantics.scl.compiler.internal.elaboration.utils.ForcedClosure;
40 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
41 import org.simantics.scl.compiler.top.SCLExpressionCompilationException;
42 import org.simantics.scl.compiler.types.TCon;
43 import org.simantics.scl.compiler.types.Type;
44 import org.simantics.scl.compiler.types.Types;
46 public class TransformationBuilder {
47 private static final TCon UMap = Types.con("Unification", "UMap");
48 private static final Name createUMap = Name.create("Unification", "createUMap");
50 private static final TCon Unifiable = Types.con("Unification", "Unifiable");
51 private static final Name uVar = Name.create("Unification", "uVar");
53 private final ErrorLog errorLog;
54 private final TypingContext context;
55 private final UnifiableFactory unifiableFactory;
58 static class Mapping {
59 LocalRelation relation;
62 THashMap<MappingRelation, Mapping> mappings = new THashMap<MappingRelation, Mapping>();
63 THashMap<TransformationRule, LocalRelation> ruleSourceMatchRelations = new THashMap<TransformationRule, LocalRelation>();
66 ArrayList<ERuleset.DatalogRule> sourceMatchingRules = new ArrayList<ERuleset.DatalogRule>();
67 ArrayList<Statement> mappingStatements = new ArrayList<Statement>();
68 TIntObjectHashMap<ArrayList<Statement>> enforcingStatements = new TIntObjectHashMap<ArrayList<Statement>>();
70 public TransformationBuilder(ErrorLog errorLog, TypingContext context) {
71 this.errorLog = errorLog;
72 this.context = context;
73 this.unifiableFactory = new UnifiableFactory(context, mappingStatements);
76 private Mapping getMapping(MappingRelation mappingRelation) {
77 Mapping mapping = mappings.get(mappingRelation);
79 mapping = new Mapping();
80 mapping.relation = new LocalRelation(mappingRelation.name.name+"_src", new Type[] {
81 mappingRelation.parameterTypes[0]
83 mapping.umap = new Variable("map_" + mappingRelation.name.name,
84 Types.apply(UMap, mappingRelation.parameterTypes)
86 mappings.put(mappingRelation, mapping);
87 mappingStatements.add(new LetStatement(new EVariable(mapping.umap),
88 Expressions.apply(context, Types.PROC, createUMap,
89 mappingRelation.parameterTypes[0],
90 mappingRelation.parameterTypes[1],
91 Expressions.punit())));
96 private static class PatternAnalyzer implements VariableProcedure {
97 THashSet<Variable> variableSet;
98 TObjectIntHashMap<Variable> mappedVariableUseCount;
99 boolean containsVariables;
101 public PatternAnalyzer(THashSet<Variable> variableSet,
102 TObjectIntHashMap<Variable> mappedVariableUseCount) {
103 this.variableSet = variableSet;
104 this.mappedVariableUseCount = mappedVariableUseCount;
108 public void execute(long location, Variable variable) {
109 if(!variableSet.contains(variable))
112 mappedVariableUseCount.adjustOrPutValue(variable, 1, 1);
114 containsVariables = true;
118 private static Expression statementsToExpression(EnvironmentalContext context, List<Statement> statements, Expression in) {
119 for(int i=statements.size()-1;i>=0;--i)
120 in = statements.get(i).toExpression(context, false, in);
124 private static Expression statementsToExpression(EnvironmentalContext context, List<Statement> statements) {
125 return statementsToExpression(context, statements, Expressions.tuple());
128 public void handleSeed(Query query) {
129 if(query instanceof QMapping) {
130 QMapping mapping = (QMapping)query;
131 Mapping m = getMapping(mapping.mappingRelation);
132 sourceMatchingRules.add(new ERuleset.DatalogRule(
135 new Expression[] {mapping.parameters[0]},
139 mappingStatements.add(new GuardStatement(unifiableFactory.putToUMapConstant(m.umap,
140 mapping.parameters[0].copy(context),
141 mapping.parameters[1].copy(context))));
143 else if(query instanceof QConjunction) {
144 QConjunction conjunction = (QConjunction)query;
145 for(Query childQuery : conjunction.queries)
146 handleSeed(childQuery);
149 errorLog.log(query.location, "Cannot use the query as a seed for the transformation.");
153 public void handleRule(TransformationRule rule) {
154 // Collect and classify queries
155 final DecomposedRule decomposed = DecomposedRule.decompose(context, rule, true);
156 for(QMapping mapping : decomposed.sourceMappings) {
157 decomposed.sourceQueries.add(new QAtom(
158 getMapping(mapping.mappingRelation).relation,
160 mapping.parameters[0].copy(context)
166 * Collect the existential variables occurring in the rule so that
167 * sourceVariables = the variables that can be solved with source patterns
168 * including the sources of the mapping relations in when section
169 * variableSet = all other existential variables that are solved in mapping/enforcing phases
171 final THashSet<Variable> variableSet = new THashSet<Variable>(rule.variables.length);
172 for(Variable variable : rule.variables)
173 variableSet.add(variable);
175 Variable[] sourceVariables;
177 final ArrayList<Variable> sourceVariableList = new ArrayList<Variable>(rule.variables.length);
178 VariableProcedure analyze = new VariableProcedure() {
180 public void execute(long location, Variable variable) {
181 if(variableSet.remove(variable))
182 sourceVariableList.add(variable);
185 for(Query query : decomposed.sourceQueries)
186 query.forVariables(analyze);
187 VariableProcedure check = new VariableProcedure() {
189 public void execute(long location, Variable variable) {
190 if(variableSet.contains(variable))
191 errorLog.log(location, "Cannot resolve the variable " + variable.getName() + " using the source patterns.");
194 for(QMapping mapping : decomposed.targetMappings)
195 mapping.parameters[0].forVariables(check);
196 sourceVariables = sourceVariableList.toArray(new Variable[sourceVariableList.size()]);
200 generateMatchingRules(decomposed, sourceVariables);
203 ArrayList<QMapping> mappings = new ArrayList<QMapping>(
204 decomposed.sourceMappings.size() + decomposed.targetMappings.size());
205 mappings.addAll(decomposed.sourceMappings);
206 mappings.addAll(decomposed.targetMappings);
209 int capacity = Math.max(10, mappings.size());
210 ArrayList<QMapping> closedMappings = new ArrayList<QMapping>(capacity);
211 ArrayList<QMapping> openMappings = new ArrayList<QMapping>(capacity);
212 ArrayList<QMapping> semiopenMappings = new ArrayList<QMapping>(capacity);
214 TObjectIntHashMap<Variable> mappedVariableUseCount = new TObjectIntHashMap<Variable>();
215 for(QMapping mapping : mappings) {
216 Expression expression = mapping.parameters[1];
217 if(expression instanceof EVariable) {
218 Variable variable = ((EVariable)expression).getVariable();
219 if(variableSet.contains(variable)) {
220 // Single open variable
221 mappedVariableUseCount.adjustOrPutValue(variable, 1, 1);
222 openMappings.add(mapping);
225 // Single variable whose value is bound
226 closedMappings.add(mapping);
230 PatternAnalyzer analyzer = new PatternAnalyzer(variableSet, mappedVariableUseCount);
231 expression.forVariables(analyzer);
233 if(analyzer.containsVariables)
234 semiopenMappings.add(mapping);
236 closedMappings.add(mapping);
240 // Generate mapping actions
241 ArrayList<Statement> phase2Actions = new ArrayList<Statement>();
242 ArrayList<Statement> phase3Actions = new ArrayList<Statement>();
243 for(QMapping mapping : closedMappings)
244 phase2Actions.add(new GuardStatement(unifiableFactory.putToUMapConstant(
245 getMapping(mapping.mappingRelation).umap,
246 mapping.parameters[0].copy(context),
247 mapping.parameters[1].copy(context))));
249 // Choose and initialize shared unification variables
250 THashMap<Variable, Variable> uniVariableMap =
251 new THashMap<Variable, Variable>();
252 for(Variable variable : mappedVariableUseCount.keySet()) {
253 int count = mappedVariableUseCount.get(variable);
255 Variable uniVariable = new Variable("uvar_" + variable.getName(),
256 Types.apply(Unifiable, variable.getType()));
257 phase2Actions.add(new LetStatement(new EVariable(uniVariable),
258 Expressions.apply(context, Types.PROC,
261 Expressions.tuple())));
262 uniVariableMap.put(variable, uniVariable);
266 // Select open mappings that use shared variables
267 THashSet<Variable> undeterminedVariables = new THashSet<Variable>(variableSet);
268 for(QMapping mapping : openMappings) {
269 Variable variable = ((EVariable)mapping.parameters[1]).getVariable();
270 if(uniVariableMap.containsKey(variable))
271 semiopenMappings.add(mapping);
273 Mapping m = getMapping(mapping.mappingRelation);
274 Type resultType = mapping.mappingRelation.parameterTypes[1];
275 phase3Actions.add(new LetStatement(new EVariable(variable),
276 unifiableFactory.getFromUMap(Expressions.var(m.umap), mapping.parameters[0].copy(context), resultType)));
277 undeterminedVariables.remove(variable);
281 for(QMapping mapping : semiopenMappings) {
282 Mapping m = getMapping(mapping.mappingRelation);
283 Type valueType = mapping.mappingRelation.parameterTypes[1];
284 phase2Actions.add(new GuardStatement(unifiableFactory.putToUMapUnifiable(
285 variableSet, uniVariableMap,
286 Expressions.var(m.umap), mapping.parameters[0].copy(context), mapping.parameters[1].copy(context))));
288 Expression pattern = toPattern(undeterminedVariables, mapping.parameters[1]);
289 if(pattern != null) {
290 Expression value = unifiableFactory.getFromUMap(Expressions.var(m.umap), mapping.parameters[0].copy(context), valueType);
291 phase3Actions.add(new LetStatement(pattern, value));
296 if(!phase2Actions.isEmpty())
297 mappingStatements.add(new GuardStatement(new EWhen(
299 new QAtom(decomposed.ruleMatchingRelation,
301 Expressions.vars(sourceVariables)),
302 statementsToExpression(context, phase2Actions),
303 sourceVariables).compile(context)));
305 // Enforcing statement
306 if(!decomposed.targetQueries.isEmpty()) {
307 for(Variable variable : rule.variables)
308 if(variableSet.contains(variable) && !mappedVariableUseCount.containsKey(variable))
309 phase3Actions.add(new LetStatement(new EVariable(variable), unifiableFactory.generateDefaultValue(variable.getType())));
311 TIntObjectHashMap<ArrayList<Query>> phases = new TIntObjectHashMap<ArrayList<Query>>();
312 for(Query targetQuery : decomposed.targetQueries)
313 targetQuery.splitToPhases(phases);
315 for(int phase : phases.keys()) {
316 ArrayList<Query> targetQuery = phases.get(phase);
317 Expression enforcing = new EEnforce(new QConjunction(targetQuery.toArray(new Query[targetQuery.size()]))).compile(context);
318 enforcing = statementsToExpression(context, phase3Actions, enforcing);
319 enforcing = new EWhen(
321 new QAtom(decomposed.ruleMatchingRelation,
323 Expressions.vars(sourceVariables)),
325 sourceVariables).compile(context);
326 ArrayList<Statement> list = enforcingStatements.get(phase);
328 list = new ArrayList<Statement>();
329 enforcingStatements.put(phase, list);
331 list.add(new GuardStatement(ForcedClosure.forceClosure(enforcing.copy(context),
332 SCLCompilerConfiguration.EVERY_RULE_ENFORCEMENT_IN_SEPARATE_METHOD)));
337 public Expression compileRules() {
338 ArrayList<LocalRelation> localRelations = new ArrayList<LocalRelation>();
339 localRelations.addAll(ruleSourceMatchRelations.values());
340 for(Mapping mapping : mappings.values())
341 localRelations.add(mapping.relation);
343 ArrayList<Statement> allEnforcingStatements;
344 if(enforcingStatements.size() == 1)
345 allEnforcingStatements = enforcingStatements.valueCollection().iterator().next();
347 int[] phases = enforcingStatements.keys();
349 allEnforcingStatements = new ArrayList<Statement>();
350 for(int phase : phases)
351 allEnforcingStatements.addAll(enforcingStatements.get(phase));
353 Expression expression = statementsToExpression(context, allEnforcingStatements);
354 expression = statementsToExpression(context, mappingStatements, expression);
357 Expression result = new ERuleset(
358 localRelations.toArray(new LocalRelation[localRelations.size()]),
359 sourceMatchingRules.toArray(new ERuleset.DatalogRule[sourceMatchingRules.size()]),
365 private Expression toPattern(
366 THashSet<Variable> undeterminedVariables,
367 Expression expression) {
368 if(expression instanceof EVariable) {
369 Variable variable = ((EVariable)expression).getVariable();
370 if(undeterminedVariables.remove(variable))
371 return new EVariable(variable);
375 if(expression instanceof EApply) {
376 EApply apply = (EApply)expression;
378 if(!(apply.getFunction() instanceof EConstant))
380 EConstant function = (EConstant)apply.getFunction();
382 IVal val = function.getValue().getValue();
383 if(!(val instanceof Constant))
385 Constant constant = (Constant)val;
387 int constructorTag = constant.constructorTag();
388 if(constructorTag < 0)
391 int arity = constant.getArity();
392 Expression[] parameters = apply.getParameters();
393 if(arity != parameters.length)
396 Expression[] patterns = new Expression[arity];
397 boolean noUndeterminedVariables = true;
398 for(int i=0;i<arity;++i) {
399 Expression pattern = toPattern(undeterminedVariables, parameters[i]);
400 patterns[i] = pattern;
402 noUndeterminedVariables = false;
404 if(noUndeterminedVariables)
407 for(int i=0;i<arity;++i)
408 if(patterns[i] == null)
409 patterns[i] = Expressions.blank(parameters[i].getType());
410 return new EApply(Locations.NO_LOCATION, apply.getEffect(), apply.getFunction().copy(context), patterns);
416 private void generateMatchingRules(DecomposedRule decomposed, Variable[] sourceVariables) {
417 // @when/from -sections
418 decomposed.ruleMatchingRelation =
419 new LocalRelation(decomposed.rule.name.name+"_match", Types.getTypes(sourceVariables));
420 ruleSourceMatchRelations.put(decomposed.rule, decomposed.ruleMatchingRelation);
421 sourceMatchingRules.add(new ERuleset.DatalogRule(decomposed.rule.location,
422 decomposed.ruleMatchingRelation,
423 Expressions.vars(sourceVariables),
424 new QConjunction(decomposed.sourceQueries.toArray(new Query[decomposed.sourceQueries.size()])),
428 for(QMapping mapping : decomposed.targetMappings)
429 sourceMatchingRules.add(new ERuleset.DatalogRule(decomposed.rule.location,
430 getMapping(mapping.mappingRelation).relation,
431 new Expression[] {mapping.parameters[0].copy(context)},
432 new QAtom(decomposed.ruleMatchingRelation,
434 Expressions.vars(sourceVariables)),