1 package org.simantics.scl.compiler.internal.elaboration.transformations;
3 import java.util.ArrayList;
4 import java.util.Arrays;
7 import org.simantics.scl.compiler.common.names.Names;
8 import org.simantics.scl.compiler.constants.Constant;
9 import org.simantics.scl.compiler.elaboration.contexts.EnvironmentalContext;
10 import org.simantics.scl.compiler.elaboration.contexts.TypingContext;
11 import org.simantics.scl.compiler.elaboration.expressions.EApply;
12 import org.simantics.scl.compiler.elaboration.expressions.EConstant;
13 import org.simantics.scl.compiler.elaboration.expressions.EEnforce;
14 import org.simantics.scl.compiler.elaboration.expressions.ERuleset;
15 import org.simantics.scl.compiler.elaboration.expressions.EVariable;
16 import org.simantics.scl.compiler.elaboration.expressions.EWhen;
17 import org.simantics.scl.compiler.elaboration.expressions.Expression;
18 import org.simantics.scl.compiler.elaboration.expressions.Expressions;
19 import org.simantics.scl.compiler.elaboration.expressions.Variable;
20 import org.simantics.scl.compiler.elaboration.expressions.VariableProcedure;
21 import org.simantics.scl.compiler.elaboration.expressions.block.GuardStatement;
22 import org.simantics.scl.compiler.elaboration.expressions.block.LetStatement;
23 import org.simantics.scl.compiler.elaboration.expressions.block.Statement;
24 import org.simantics.scl.compiler.elaboration.query.QAtom;
25 import org.simantics.scl.compiler.elaboration.query.QConjunction;
26 import org.simantics.scl.compiler.elaboration.query.QMapping;
27 import org.simantics.scl.compiler.elaboration.query.Query;
28 import org.simantics.scl.compiler.elaboration.relations.LocalRelation;
29 import org.simantics.scl.compiler.elaboration.rules.MappingRelation;
30 import org.simantics.scl.compiler.elaboration.rules.TransformationRule;
31 import org.simantics.scl.compiler.errors.ErrorLog;
32 import org.simantics.scl.compiler.errors.Locations;
33 import org.simantics.scl.compiler.internal.codegen.references.IVal;
34 import org.simantics.scl.compiler.internal.elaboration.utils.ForcedClosure;
35 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
36 import org.simantics.scl.compiler.types.Type;
37 import org.simantics.scl.compiler.types.Types;
39 import gnu.trove.map.hash.THashMap;
40 import gnu.trove.map.hash.TIntObjectHashMap;
41 import gnu.trove.map.hash.TObjectIntHashMap;
42 import gnu.trove.set.hash.THashSet;
44 public class TransformationBuilder {
45 private final ErrorLog errorLog;
46 private final TypingContext context;
47 private final UnifiableFactory unifiableFactory;
50 static class Mapping {
51 LocalRelation relation;
54 THashMap<MappingRelation, Mapping> mappings = new THashMap<MappingRelation, Mapping>();
55 THashMap<TransformationRule, LocalRelation> ruleSourceMatchRelations = new THashMap<TransformationRule, LocalRelation>();
58 ArrayList<ERuleset.DatalogRule> sourceMatchingRules = new ArrayList<ERuleset.DatalogRule>();
59 ArrayList<Statement> mappingStatements = new ArrayList<Statement>();
60 TIntObjectHashMap<ArrayList<Statement>> enforcingStatements = new TIntObjectHashMap<ArrayList<Statement>>();
62 public TransformationBuilder(ErrorLog errorLog, TypingContext context) {
63 this.errorLog = errorLog;
64 this.context = context;
65 this.unifiableFactory = new UnifiableFactory(context, mappingStatements);
68 private Mapping getMapping(MappingRelation mappingRelation) {
69 Mapping mapping = mappings.get(mappingRelation);
71 mapping = new Mapping();
72 mapping.relation = new LocalRelation(mappingRelation.name.name+"_src", new Type[] {
73 mappingRelation.parameterTypes[0]
75 mapping.umap = new Variable("map_" + mappingRelation.name.name,
76 Types.apply(Names.Unifiable_UMap, mappingRelation.parameterTypes)
78 mappings.put(mappingRelation, mapping);
79 mappingStatements.add(new LetStatement(new EVariable(mapping.umap),
80 Expressions.apply(context.getCompilationContext(), Types.PROC, Names.Unifiable_createUMap,
81 mappingRelation.parameterTypes[0],
82 mappingRelation.parameterTypes[1],
83 Expressions.punit())));
88 private static class PatternAnalyzer implements VariableProcedure {
89 THashSet<Variable> variableSet;
90 TObjectIntHashMap<Variable> mappedVariableUseCount;
91 boolean containsVariables;
93 public PatternAnalyzer(THashSet<Variable> variableSet,
94 TObjectIntHashMap<Variable> mappedVariableUseCount) {
95 this.variableSet = variableSet;
96 this.mappedVariableUseCount = mappedVariableUseCount;
100 public void execute(long location, Variable variable) {
101 if(!variableSet.contains(variable))
104 mappedVariableUseCount.adjustOrPutValue(variable, 1, 1);
106 containsVariables = true;
110 private static Expression statementsToExpression(EnvironmentalContext context, List<Statement> statements, Expression in) {
111 for(int i=statements.size()-1;i>=0;--i)
112 in = statements.get(i).toExpression(context, false, in);
116 private static Expression statementsToExpression(EnvironmentalContext context, List<Statement> statements) {
117 return statementsToExpression(context, statements, Expressions.tuple());
120 public void handleSeed(Query query) {
121 if(query instanceof QMapping) {
122 QMapping mapping = (QMapping)query;
123 Mapping m = getMapping(mapping.mappingRelation);
124 sourceMatchingRules.add(new ERuleset.DatalogRule(
127 new Expression[] {mapping.parameters[0]},
131 mappingStatements.add(new GuardStatement(unifiableFactory.putToUMapConstant(m.umap,
132 mapping.parameters[0].copy(context),
133 mapping.parameters[1].copy(context))));
135 else if(query instanceof QConjunction) {
136 QConjunction conjunction = (QConjunction)query;
137 for(Query childQuery : conjunction.queries)
138 handleSeed(childQuery);
141 errorLog.log(query.location, "Cannot use the query as a seed for the transformation.");
145 public void handleRule(TransformationRule rule) {
146 // Collect and classify queries
147 final DecomposedRule decomposed = DecomposedRule.decompose(context, rule, true);
148 for(QMapping mapping : decomposed.sourceMappings) {
149 decomposed.sourceQueries.add(new QAtom(
150 getMapping(mapping.mappingRelation).relation,
152 mapping.parameters[0].copy(context)
158 * Collect the existential variables occurring in the rule so that
159 * sourceVariables = the variables that can be solved with source patterns
160 * including the sources of the mapping relations in when section
161 * variableSet = all other existential variables that are solved in mapping/enforcing phases
163 final THashSet<Variable> variableSet = new THashSet<Variable>(rule.variables.length);
164 for(Variable variable : rule.variables)
165 variableSet.add(variable);
167 Variable[] sourceVariables;
169 final ArrayList<Variable> sourceVariableList = new ArrayList<Variable>(rule.variables.length);
170 VariableProcedure analyze = new VariableProcedure() {
172 public void execute(long location, Variable variable) {
173 if(variableSet.remove(variable))
174 sourceVariableList.add(variable);
177 for(Query query : decomposed.sourceQueries)
178 query.forVariables(analyze);
179 VariableProcedure check = new VariableProcedure() {
181 public void execute(long location, Variable variable) {
182 if(variableSet.contains(variable))
183 errorLog.log(location, "Cannot resolve the variable " + variable.getName() + " using the source patterns.");
186 for(QMapping mapping : decomposed.targetMappings)
187 mapping.parameters[0].forVariables(check);
188 sourceVariables = sourceVariableList.toArray(new Variable[sourceVariableList.size()]);
192 generateMatchingRules(decomposed, sourceVariables);
195 ArrayList<QMapping> mappings = new ArrayList<QMapping>(
196 decomposed.sourceMappings.size() + decomposed.targetMappings.size());
197 mappings.addAll(decomposed.sourceMappings);
198 mappings.addAll(decomposed.targetMappings);
201 int capacity = Math.max(10, mappings.size());
202 ArrayList<QMapping> closedMappings = new ArrayList<QMapping>(capacity);
203 ArrayList<QMapping> openMappings = new ArrayList<QMapping>(capacity);
204 ArrayList<QMapping> semiopenMappings = new ArrayList<QMapping>(capacity);
206 TObjectIntHashMap<Variable> mappedVariableUseCount = new TObjectIntHashMap<Variable>();
207 for(QMapping mapping : mappings) {
208 Expression expression = mapping.parameters[1];
209 if(expression instanceof EVariable) {
210 Variable variable = ((EVariable)expression).getVariable();
211 if(variableSet.contains(variable)) {
212 // Single open variable
213 mappedVariableUseCount.adjustOrPutValue(variable, 1, 1);
214 openMappings.add(mapping);
217 // Single variable whose value is bound
218 closedMappings.add(mapping);
222 PatternAnalyzer analyzer = new PatternAnalyzer(variableSet, mappedVariableUseCount);
223 expression.forVariables(analyzer);
225 if(analyzer.containsVariables)
226 semiopenMappings.add(mapping);
228 closedMappings.add(mapping);
232 // Generate mapping actions
233 ArrayList<Statement> phase2Actions = new ArrayList<Statement>();
234 ArrayList<Statement> phase3Actions = new ArrayList<Statement>();
235 for(QMapping mapping : closedMappings)
236 phase2Actions.add(new GuardStatement(unifiableFactory.putToUMapConstant(
237 getMapping(mapping.mappingRelation).umap,
238 mapping.parameters[0].copy(context),
239 mapping.parameters[1].copy(context))));
241 // Choose and initialize shared unification variables
242 THashMap<Variable, Variable> uniVariableMap =
243 new THashMap<Variable, Variable>();
244 for(Variable variable : mappedVariableUseCount.keySet()) {
245 int count = mappedVariableUseCount.get(variable);
247 Variable uniVariable = new Variable("uvar_" + variable.getName(),
248 Types.apply(Names.Unifiable_Unifiable, variable.getType()));
249 phase2Actions.add(new LetStatement(new EVariable(uniVariable),
250 Expressions.apply(context.getCompilationContext(), Types.PROC,
251 Names.Unifiable_uVar,
253 Expressions.tuple())));
254 uniVariableMap.put(variable, uniVariable);
258 // Select open mappings that use shared variables
259 THashSet<Variable> undeterminedVariables = new THashSet<Variable>(variableSet);
260 for(QMapping mapping : openMappings) {
261 Variable variable = ((EVariable)mapping.parameters[1]).getVariable();
262 if(uniVariableMap.containsKey(variable))
263 semiopenMappings.add(mapping);
265 Mapping m = getMapping(mapping.mappingRelation);
266 Type resultType = mapping.mappingRelation.parameterTypes[1];
267 phase3Actions.add(new LetStatement(new EVariable(variable),
268 unifiableFactory.getFromUMap(Expressions.var(m.umap), mapping.parameters[0].copy(context), resultType)));
269 undeterminedVariables.remove(variable);
273 for(QMapping mapping : semiopenMappings) {
274 Mapping m = getMapping(mapping.mappingRelation);
275 Type valueType = mapping.mappingRelation.parameterTypes[1];
276 phase2Actions.add(new GuardStatement(unifiableFactory.putToUMapUnifiable(
277 variableSet, uniVariableMap,
278 Expressions.var(m.umap), mapping.parameters[0].copy(context), mapping.parameters[1].copy(context))));
280 Expression pattern = toPattern(undeterminedVariables, mapping.parameters[1]);
281 if(pattern != null) {
282 Expression value = unifiableFactory.getFromUMap(Expressions.var(m.umap), mapping.parameters[0].copy(context), valueType);
283 phase3Actions.add(new LetStatement(pattern, value));
288 if(!phase2Actions.isEmpty())
289 mappingStatements.add(new GuardStatement(new EWhen(
291 new QAtom(decomposed.ruleMatchingRelation,
293 Expressions.vars(sourceVariables)),
294 statementsToExpression(context.getCompilationContext(), phase2Actions),
295 sourceVariables).compile(context)));
297 // Enforcing statement
298 if(!decomposed.targetQueries.isEmpty()) {
299 for(Variable variable : rule.variables)
300 if(variableSet.contains(variable) && !mappedVariableUseCount.containsKey(variable))
301 phase3Actions.add(new LetStatement(new EVariable(variable), unifiableFactory.generateDefaultValue(variable.getType())));
303 TIntObjectHashMap<ArrayList<Query>> phases = new TIntObjectHashMap<ArrayList<Query>>();
304 for(Query targetQuery : decomposed.targetQueries)
305 targetQuery.splitToPhases(phases);
307 for(int phase : phases.keys()) {
308 ArrayList<Query> targetQuery = phases.get(phase);
309 Expression enforcing = new EEnforce(new QConjunction(targetQuery.toArray(new Query[targetQuery.size()]))).compile(context);
310 enforcing = statementsToExpression(context.getCompilationContext(), phase3Actions, enforcing);
311 enforcing = new EWhen(
313 new QAtom(decomposed.ruleMatchingRelation,
315 Expressions.vars(sourceVariables)),
317 sourceVariables).compile(context);
318 ArrayList<Statement> list = enforcingStatements.get(phase);
320 list = new ArrayList<Statement>();
321 enforcingStatements.put(phase, list);
323 list.add(new GuardStatement(ForcedClosure.forceClosure(enforcing.copy(context),
324 SCLCompilerConfiguration.EVERY_RULE_ENFORCEMENT_IN_SEPARATE_METHOD)));
329 public Expression compileRules() {
330 ArrayList<LocalRelation> localRelations = new ArrayList<LocalRelation>();
331 localRelations.addAll(ruleSourceMatchRelations.values());
332 for(Mapping mapping : mappings.values())
333 localRelations.add(mapping.relation);
335 ArrayList<Statement> allEnforcingStatements;
336 if(enforcingStatements.size() == 1)
337 allEnforcingStatements = enforcingStatements.valueCollection().iterator().next();
339 int[] phases = enforcingStatements.keys();
341 allEnforcingStatements = new ArrayList<Statement>();
342 for(int phase : phases)
343 allEnforcingStatements.addAll(enforcingStatements.get(phase));
345 Expression expression = statementsToExpression(context.getCompilationContext(), allEnforcingStatements);
346 expression = statementsToExpression(context.getCompilationContext(), mappingStatements, expression);
349 Expression result = new ERuleset(
350 localRelations.toArray(new LocalRelation[localRelations.size()]),
351 sourceMatchingRules.toArray(new ERuleset.DatalogRule[sourceMatchingRules.size()]),
357 private Expression toPattern(
358 THashSet<Variable> undeterminedVariables,
359 Expression expression) {
360 if(expression instanceof EVariable) {
361 Variable variable = ((EVariable)expression).getVariable();
362 if(undeterminedVariables.remove(variable))
363 return new EVariable(variable);
367 if(expression instanceof EApply) {
368 EApply apply = (EApply)expression;
370 if(!(apply.getFunction() instanceof EConstant))
372 EConstant function = (EConstant)apply.getFunction();
374 IVal val = function.getValue().getValue();
375 if(!(val instanceof Constant))
377 Constant constant = (Constant)val;
379 int constructorTag = constant.constructorTag();
380 if(constructorTag < 0)
383 int arity = constant.getArity();
384 Expression[] parameters = apply.getParameters();
385 if(arity != parameters.length)
388 Expression[] patterns = new Expression[arity];
389 boolean noUndeterminedVariables = true;
390 for(int i=0;i<arity;++i) {
391 Expression pattern = toPattern(undeterminedVariables, parameters[i]);
392 patterns[i] = pattern;
394 noUndeterminedVariables = false;
396 if(noUndeterminedVariables)
399 for(int i=0;i<arity;++i)
400 if(patterns[i] == null)
401 patterns[i] = Expressions.blank(parameters[i].getType());
402 return new EApply(Locations.NO_LOCATION, apply.getEffect(), apply.getFunction().copy(context), patterns);
408 private void generateMatchingRules(DecomposedRule decomposed, Variable[] sourceVariables) {
409 // @when/from -sections
410 decomposed.ruleMatchingRelation =
411 new LocalRelation(decomposed.rule.name.name+"_match", Types.getTypes(sourceVariables));
412 ruleSourceMatchRelations.put(decomposed.rule, decomposed.ruleMatchingRelation);
413 sourceMatchingRules.add(new ERuleset.DatalogRule(decomposed.rule.location,
414 decomposed.ruleMatchingRelation,
415 Expressions.vars(sourceVariables),
416 new QConjunction(decomposed.sourceQueries.toArray(new Query[decomposed.sourceQueries.size()])),
420 for(QMapping mapping : decomposed.targetMappings)
421 sourceMatchingRules.add(new ERuleset.DatalogRule(decomposed.rule.location,
422 getMapping(mapping.mappingRelation).relation,
423 new Expression[] {mapping.parameters[0].copy(context)},
424 new QAtom(decomposed.ruleMatchingRelation,
426 Expressions.vars(sourceVariables)),