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.BlockType;
22 import org.simantics.scl.compiler.elaboration.expressions.block.GuardStatement;
23 import org.simantics.scl.compiler.elaboration.expressions.block.LetStatement;
24 import org.simantics.scl.compiler.elaboration.expressions.block.Statement;
25 import org.simantics.scl.compiler.elaboration.query.QAtom;
26 import org.simantics.scl.compiler.elaboration.query.QConjunction;
27 import org.simantics.scl.compiler.elaboration.query.QMapping;
28 import org.simantics.scl.compiler.elaboration.query.Query;
29 import org.simantics.scl.compiler.elaboration.relations.LocalRelation;
30 import org.simantics.scl.compiler.elaboration.rules.MappingRelation;
31 import org.simantics.scl.compiler.elaboration.rules.TransformationRule;
32 import org.simantics.scl.compiler.errors.ErrorLog;
33 import org.simantics.scl.compiler.errors.Locations;
34 import org.simantics.scl.compiler.internal.codegen.references.IVal;
35 import org.simantics.scl.compiler.internal.elaboration.utils.ForcedClosure;
36 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
37 import org.simantics.scl.compiler.types.Type;
38 import org.simantics.scl.compiler.types.Types;
40 import gnu.trove.map.hash.THashMap;
41 import gnu.trove.map.hash.TIntObjectHashMap;
42 import gnu.trove.map.hash.TObjectIntHashMap;
43 import gnu.trove.set.hash.THashSet;
45 public class TransformationBuilder {
46 private final ErrorLog errorLog;
47 private final TypingContext context;
48 private final UnifiableFactory unifiableFactory;
51 static class Mapping {
52 LocalRelation relation;
55 THashMap<MappingRelation, Mapping> mappings = new THashMap<MappingRelation, Mapping>();
56 THashMap<TransformationRule, LocalRelation> ruleSourceMatchRelations = new THashMap<TransformationRule, LocalRelation>();
59 ArrayList<ERuleset.DatalogRule> sourceMatchingRules = new ArrayList<ERuleset.DatalogRule>();
60 ArrayList<Statement> mappingStatements = new ArrayList<Statement>();
61 TIntObjectHashMap<ArrayList<Statement>> enforcingStatements = new TIntObjectHashMap<ArrayList<Statement>>();
63 public TransformationBuilder(ErrorLog errorLog, TypingContext context) {
64 this.errorLog = errorLog;
65 this.context = context;
66 this.unifiableFactory = new UnifiableFactory(context, mappingStatements);
69 private Mapping getMapping(MappingRelation mappingRelation) {
70 Mapping mapping = mappings.get(mappingRelation);
72 mapping = new Mapping();
73 mapping.relation = new LocalRelation(mappingRelation.name.name+"_src", new Type[] {
74 mappingRelation.parameterTypes[0]
76 mapping.umap = new Variable("map_" + mappingRelation.name.name,
77 Types.apply(Names.Unifiable_UMap, mappingRelation.parameterTypes)
79 mappings.put(mappingRelation, mapping);
80 mappingStatements.add(new LetStatement(new EVariable(mapping.umap),
81 Expressions.apply(context.getCompilationContext(), Types.PROC, Names.Unifiable_createUMap,
82 mappingRelation.parameterTypes[0],
83 mappingRelation.parameterTypes[1],
84 Expressions.punit())));
89 private static class PatternAnalyzer implements VariableProcedure {
90 THashSet<Variable> variableSet;
91 TObjectIntHashMap<Variable> mappedVariableUseCount;
92 boolean containsVariables;
94 public PatternAnalyzer(THashSet<Variable> variableSet,
95 TObjectIntHashMap<Variable> mappedVariableUseCount) {
96 this.variableSet = variableSet;
97 this.mappedVariableUseCount = mappedVariableUseCount;
101 public void execute(long location, Variable variable) {
102 if(!variableSet.contains(variable))
105 mappedVariableUseCount.adjustOrPutValue(variable, 1, 1);
107 containsVariables = true;
111 private static Expression statementsToExpression(EnvironmentalContext context, List<Statement> statements, Expression in) {
112 for(int i=statements.size()-1;i>=0;--i)
113 in = statements.get(i).toExpression(context, BlockType.Normal, in);
117 private static Expression statementsToExpression(EnvironmentalContext context, List<Statement> statements) {
118 return statementsToExpression(context, statements, Expressions.tuple());
121 public void handleSeed(Query query) {
122 if(query instanceof QMapping) {
123 QMapping mapping = (QMapping)query;
124 Mapping m = getMapping(mapping.mappingRelation);
125 sourceMatchingRules.add(new ERuleset.DatalogRule(
128 new Expression[] {mapping.parameters[0]},
132 mappingStatements.add(new GuardStatement(unifiableFactory.putToUMapConstant(m.umap,
133 mapping.parameters[0].copy(context),
134 mapping.parameters[1].copy(context))));
136 else if(query instanceof QConjunction) {
137 QConjunction conjunction = (QConjunction)query;
138 for(Query childQuery : conjunction.queries)
139 handleSeed(childQuery);
142 errorLog.log(query.location, "Cannot use the query as a seed for the transformation.");
146 public void handleRule(TransformationRule rule) {
147 // Collect and classify queries
148 final DecomposedRule decomposed = DecomposedRule.decompose(context, rule, true);
149 for(QMapping mapping : decomposed.sourceMappings) {
150 decomposed.sourceQueries.add(new QAtom(
151 getMapping(mapping.mappingRelation).relation,
153 mapping.parameters[0].copy(context)
159 * Collect the existential variables occurring in the rule so that
160 * sourceVariables = the variables that can be solved with source patterns
161 * including the sources of the mapping relations in when section
162 * variableSet = all other existential variables that are solved in mapping/enforcing phases
164 final THashSet<Variable> variableSet = new THashSet<Variable>(rule.variables.length);
165 for(Variable variable : rule.variables)
166 variableSet.add(variable);
168 Variable[] sourceVariables;
170 final ArrayList<Variable> sourceVariableList = new ArrayList<Variable>(rule.variables.length);
171 VariableProcedure analyze = new VariableProcedure() {
173 public void execute(long location, Variable variable) {
174 if(variableSet.remove(variable))
175 sourceVariableList.add(variable);
178 for(Query query : decomposed.sourceQueries)
179 query.forVariables(analyze);
180 VariableProcedure check = new VariableProcedure() {
182 public void execute(long location, Variable variable) {
183 if(variableSet.contains(variable))
184 errorLog.log(location, "Cannot resolve the variable " + variable.getName() + " using the source patterns.");
187 for(QMapping mapping : decomposed.targetMappings)
188 mapping.parameters[0].forVariableUses(check);
189 sourceVariables = sourceVariableList.toArray(new Variable[sourceVariableList.size()]);
193 generateMatchingRules(decomposed, sourceVariables);
196 ArrayList<QMapping> mappings = new ArrayList<QMapping>(
197 decomposed.sourceMappings.size() + decomposed.targetMappings.size());
198 mappings.addAll(decomposed.sourceMappings);
199 mappings.addAll(decomposed.targetMappings);
202 int capacity = Math.max(10, mappings.size());
203 ArrayList<QMapping> closedMappings = new ArrayList<QMapping>(capacity);
204 ArrayList<QMapping> openMappings = new ArrayList<QMapping>(capacity);
205 ArrayList<QMapping> semiopenMappings = new ArrayList<QMapping>(capacity);
207 TObjectIntHashMap<Variable> mappedVariableUseCount = new TObjectIntHashMap<Variable>();
208 for(QMapping mapping : mappings) {
209 Expression expression = mapping.parameters[1];
210 if(expression instanceof EVariable) {
211 Variable variable = ((EVariable)expression).getVariable();
212 if(variableSet.contains(variable)) {
213 // Single open variable
214 mappedVariableUseCount.adjustOrPutValue(variable, 1, 1);
215 openMappings.add(mapping);
218 // Single variable whose value is bound
219 closedMappings.add(mapping);
223 PatternAnalyzer analyzer = new PatternAnalyzer(variableSet, mappedVariableUseCount);
224 expression.forVariableUses(analyzer);
226 if(analyzer.containsVariables)
227 semiopenMappings.add(mapping);
229 closedMappings.add(mapping);
233 // Generate mapping actions
234 ArrayList<Statement> phase2Actions = new ArrayList<Statement>();
235 ArrayList<Statement> phase3Actions = new ArrayList<Statement>();
236 for(QMapping mapping : closedMappings)
237 phase2Actions.add(new GuardStatement(unifiableFactory.putToUMapConstant(
238 getMapping(mapping.mappingRelation).umap,
239 mapping.parameters[0].copy(context),
240 mapping.parameters[1].copy(context))));
242 // Choose and initialize shared unification variables
243 THashMap<Variable, Variable> uniVariableMap =
244 new THashMap<Variable, Variable>();
245 for(Variable variable : mappedVariableUseCount.keySet()) {
246 int count = mappedVariableUseCount.get(variable);
248 Variable uniVariable = new Variable("uvar_" + variable.getName(),
249 Types.apply(Names.Unifiable_Unifiable, variable.getType()));
250 phase2Actions.add(new LetStatement(new EVariable(uniVariable),
251 Expressions.apply(context.getCompilationContext(), Types.PROC,
252 Names.Unifiable_uVar,
254 Expressions.tuple())));
255 uniVariableMap.put(variable, uniVariable);
259 // Select open mappings that use shared variables
260 THashSet<Variable> undeterminedVariables = new THashSet<Variable>(variableSet);
261 for(QMapping mapping : openMappings) {
262 Variable variable = ((EVariable)mapping.parameters[1]).getVariable();
263 if(uniVariableMap.containsKey(variable))
264 semiopenMappings.add(mapping);
266 Mapping m = getMapping(mapping.mappingRelation);
267 Type resultType = mapping.mappingRelation.parameterTypes[1];
268 phase3Actions.add(new LetStatement(new EVariable(variable),
269 unifiableFactory.getFromUMap(Expressions.var(m.umap), mapping.parameters[0].copy(context), resultType)));
270 undeterminedVariables.remove(variable);
274 for(QMapping mapping : semiopenMappings) {
275 Mapping m = getMapping(mapping.mappingRelation);
276 Type valueType = mapping.mappingRelation.parameterTypes[1];
277 phase2Actions.add(new GuardStatement(unifiableFactory.putToUMapUnifiable(
278 variableSet, uniVariableMap,
279 Expressions.var(m.umap), mapping.parameters[0].copy(context), mapping.parameters[1].copy(context))));
281 Expression pattern = toPattern(undeterminedVariables, mapping.parameters[1]);
282 if(pattern != null) {
283 Expression value = unifiableFactory.getFromUMap(Expressions.var(m.umap), mapping.parameters[0].copy(context), valueType);
284 phase3Actions.add(new LetStatement(pattern, value));
289 if(!phase2Actions.isEmpty())
290 mappingStatements.add(new GuardStatement(new EWhen(
292 new QAtom(decomposed.ruleMatchingRelation,
294 Expressions.vars(sourceVariables)),
295 statementsToExpression(context.getCompilationContext(), phase2Actions),
296 sourceVariables).compile(context)));
298 // Enforcing statement
299 if(!decomposed.targetQueries.isEmpty()) {
300 for(Variable variable : rule.variables)
301 if(variableSet.contains(variable) && !mappedVariableUseCount.containsKey(variable))
302 phase3Actions.add(new LetStatement(new EVariable(variable), unifiableFactory.generateDefaultValue(variable.getType())));
304 TIntObjectHashMap<ArrayList<Query>> phases = new TIntObjectHashMap<ArrayList<Query>>();
305 for(Query targetQuery : decomposed.targetQueries)
306 targetQuery.splitToPhases(phases);
308 for(int phase : phases.keys()) {
309 ArrayList<Query> targetQuery = phases.get(phase);
310 Expression enforcing = new EEnforce(new QConjunction(targetQuery.toArray(new Query[targetQuery.size()]))).compile(context);
311 enforcing = statementsToExpression(context.getCompilationContext(), phase3Actions, enforcing);
312 enforcing = new EWhen(
314 new QAtom(decomposed.ruleMatchingRelation,
316 Expressions.vars(sourceVariables)),
318 sourceVariables).compile(context);
319 ArrayList<Statement> list = enforcingStatements.get(phase);
321 list = new ArrayList<Statement>();
322 enforcingStatements.put(phase, list);
324 list.add(new GuardStatement(ForcedClosure.forceClosure(enforcing.copy(context),
325 SCLCompilerConfiguration.EVERY_RULE_ENFORCEMENT_IN_SEPARATE_METHOD)));
330 public Expression compileRules() {
331 ArrayList<LocalRelation> localRelations = new ArrayList<LocalRelation>();
332 localRelations.addAll(ruleSourceMatchRelations.values());
333 for(Mapping mapping : mappings.values())
334 localRelations.add(mapping.relation);
336 ArrayList<Statement> allEnforcingStatements;
337 if(enforcingStatements.size() == 1)
338 allEnforcingStatements = enforcingStatements.valueCollection().iterator().next();
340 int[] phases = enforcingStatements.keys();
342 allEnforcingStatements = new ArrayList<Statement>();
343 for(int phase : phases)
344 allEnforcingStatements.addAll(enforcingStatements.get(phase));
346 Expression expression = statementsToExpression(context.getCompilationContext(), allEnforcingStatements);
347 expression = statementsToExpression(context.getCompilationContext(), mappingStatements, expression);
350 Expression result = new ERuleset(
351 localRelations.toArray(new LocalRelation[localRelations.size()]),
352 sourceMatchingRules.toArray(new ERuleset.DatalogRule[sourceMatchingRules.size()]),
358 private Expression toPattern(
359 THashSet<Variable> undeterminedVariables,
360 Expression expression) {
361 if(expression instanceof EVariable) {
362 Variable variable = ((EVariable)expression).getVariable();
363 if(undeterminedVariables.remove(variable))
364 return new EVariable(variable);
368 if(expression instanceof EApply) {
369 EApply apply = (EApply)expression;
371 if(!(apply.getFunction() instanceof EConstant))
373 EConstant function = (EConstant)apply.getFunction();
375 IVal val = function.getValue().getValue();
376 if(!(val instanceof Constant))
378 Constant constant = (Constant)val;
380 int constructorTag = constant.constructorTag();
381 if(constructorTag < 0)
384 int arity = constant.getArity();
385 Expression[] parameters = apply.getParameters();
386 if(arity != parameters.length)
389 Expression[] patterns = new Expression[arity];
390 boolean noUndeterminedVariables = true;
391 for(int i=0;i<arity;++i) {
392 Expression pattern = toPattern(undeterminedVariables, parameters[i]);
393 patterns[i] = pattern;
395 noUndeterminedVariables = false;
397 if(noUndeterminedVariables)
400 for(int i=0;i<arity;++i)
401 if(patterns[i] == null)
402 patterns[i] = Expressions.blank(parameters[i].getType());
403 return new EApply(Locations.NO_LOCATION, apply.getEffect(), apply.getFunction().copy(context), patterns);
409 private void generateMatchingRules(DecomposedRule decomposed, Variable[] sourceVariables) {
410 // @when/from -sections
411 decomposed.ruleMatchingRelation =
412 new LocalRelation(decomposed.rule.name.name+"_match", Types.getTypes(sourceVariables));
413 ruleSourceMatchRelations.put(decomposed.rule, decomposed.ruleMatchingRelation);
414 sourceMatchingRules.add(new ERuleset.DatalogRule(decomposed.rule.location,
415 decomposed.ruleMatchingRelation,
416 Expressions.vars(sourceVariables),
417 new QConjunction(decomposed.sourceQueries.toArray(new Query[decomposed.sourceQueries.size()])),
421 for(QMapping mapping : decomposed.targetMappings)
422 sourceMatchingRules.add(new ERuleset.DatalogRule(decomposed.rule.location,
423 getMapping(mapping.mappingRelation).relation,
424 new Expression[] {mapping.parameters[0].copy(context)},
425 new QAtom(decomposed.ruleMatchingRelation,
427 Expressions.vars(sourceVariables)),