1 package org.simantics.scl.compiler.internal.elaboration.matching;
3 import java.util.ArrayList;
4 import java.util.Arrays;
7 import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
8 import org.simantics.scl.compiler.compilation.CompilationContext;
9 import org.simantics.scl.compiler.constants.Constant;
10 import org.simantics.scl.compiler.elaboration.expressions.EApply;
11 import org.simantics.scl.compiler.elaboration.expressions.EApplyType;
12 import org.simantics.scl.compiler.elaboration.expressions.EAsPattern;
13 import org.simantics.scl.compiler.elaboration.expressions.EConstant;
14 import org.simantics.scl.compiler.elaboration.expressions.EExternalConstant;
15 import org.simantics.scl.compiler.elaboration.expressions.ELiteral;
16 import org.simantics.scl.compiler.elaboration.expressions.EVariable;
17 import org.simantics.scl.compiler.elaboration.expressions.EViewPattern;
18 import org.simantics.scl.compiler.elaboration.expressions.Expression;
19 import org.simantics.scl.compiler.elaboration.expressions.GuardedExpressionGroup;
20 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
21 import org.simantics.scl.compiler.elaboration.modules.TypeConstructor;
22 import org.simantics.scl.compiler.internal.codegen.continuations.Branch;
23 import org.simantics.scl.compiler.internal.codegen.continuations.ICont;
24 import org.simantics.scl.compiler.internal.codegen.references.IVal;
25 import org.simantics.scl.compiler.internal.codegen.writer.CodeWriter;
26 import org.simantics.scl.compiler.types.TCon;
27 import org.simantics.scl.compiler.types.Types;
28 import org.simantics.scl.compiler.types.exceptions.MatchException;
30 import gnu.trove.map.hash.THashMap;
32 public class PatternMatchingCompiler {
34 private static class ExpressionMatrix {
36 final IVal[] scrutinee;
37 final List<Row> rows = new ArrayList<Row>();
39 public ExpressionMatrix(CodeWriter w, IVal[] scrutinee) {
41 this.scrutinee = scrutinee;
45 public static IVal[] replace(IVal[] vals, int columnToReplace, IVal ... substitution) {
46 IVal[] newVals = new IVal[vals.length-1+substitution.length];
48 for(int i=0;i<columnToReplace;++i)
49 newVals[j++] = vals[i];
50 for(int i=0;i<substitution.length;++i)
51 newVals[j++] = substitution[i];
52 for(int i=columnToReplace+1;i<vals.length;++i)
53 newVals[j++] = vals[i];
57 private static void splitByConstructors(CodeWriter w, final CompilationContext context, IVal[] scrutinee, final ICont success, ICont failure, List<Row> rows, int columnId) {
58 THashMap<Object, ExpressionMatrix> matrixMap = new THashMap<Object, ExpressionMatrix>();
59 ArrayList<Branch> branches = new ArrayList<Branch>();
60 ArrayList<ExpressionMatrix> matrices = new ArrayList<ExpressionMatrix>();
62 /*System.out.println("---");
64 for(Expression e : row.patterns)
65 System.out.print(e + " ");
70 for(i=0;i<rows.size();++i) {
71 Row row = rows.get(i);
72 Expression pattern = row.patterns[columnId];
74 if(pattern instanceof EApplyType)
75 pattern = ((EApplyType)pattern).getExpression();
76 else if(pattern instanceof EAsPattern) {
77 EAsPattern asPattern = (EAsPattern)pattern;
78 pattern = asPattern.getPattern();
79 asPattern.getVariable().setVal(scrutinee[columnId]);
83 row.patterns[columnId] = pattern;
85 if(pattern instanceof EVariable)
87 else if(pattern instanceof EApply) {
88 EApply applyConstructor = (EApply)pattern;
89 Expression constructor_ = applyConstructor.getFunction();
90 while(constructor_ instanceof EApplyType)
91 constructor_ = ((EApplyType)constructor_).getExpression();
92 Expression[] parameters = applyConstructor.getParameters();
93 // TODO How type parameters are handled???
94 if(constructor_ instanceof EConstant) {
95 SCLValue constructor = ((EConstant)constructor_).getValue();
97 ExpressionMatrix matrix = matrixMap.get(constructor.getName());
99 CodeWriter newW = w.createBlock(Types.getTypes(parameters));
100 branches.add(new Branch((Constant)constructor.getValue(), newW.getContinuation()));
101 matrix = new ExpressionMatrix(newW, replace(scrutinee, columnId, newW.getParameters()));
102 matrices.add(matrix);
103 matrixMap.put(constructor.getName(), matrix);
105 matrix.rows.add(row.replace(columnId, parameters));
107 else if(constructor_ instanceof ELiteral) {
108 Constant constructor = ((ELiteral)constructor_).getValue();
110 ExpressionMatrix matrix = matrixMap.get(constructor);
112 CodeWriter newW = w.createBlock(Types.getTypes(parameters));
113 branches.add(new Branch(constructor, newW.getContinuation()));
114 matrix = new ExpressionMatrix(newW, replace(scrutinee, columnId, newW.getParameters()));
115 matrices.add(matrix);
116 matrixMap.put(constructor, matrix);
118 matrix.rows.add(row.replace(columnId, parameters));
121 else if(pattern instanceof EConstant) {
122 EConstant applyConstructor = (EConstant)pattern;
123 SCLValue constructor = applyConstructor.getValue();
125 ExpressionMatrix matrix = matrixMap.get(constructor.getName());
127 CodeWriter newW = w.createBlock();
128 branches.add(new Branch((Constant)constructor.getValue(), newW.getContinuation()));
129 matrix = new ExpressionMatrix(newW, replace(scrutinee, columnId, newW.getParameters()));
130 matrices.add(matrix);
131 matrixMap.put(constructor.getName(), matrix);
133 matrix.rows.add(row.replace(columnId, Expression.EMPTY_ARRAY));
135 else if(pattern instanceof ELiteral) {
136 ELiteral literal = (ELiteral)pattern;
137 Constant constructor = literal.getValue();
139 ExpressionMatrix matrix = matrixMap.get(constructor);
141 CodeWriter newW = w.createBlock();
142 branches.add(new Branch(constructor, newW.getContinuation()));
143 matrix = new ExpressionMatrix(newW, replace(scrutinee, columnId, newW.getParameters()));
144 matrices.add(matrix);
145 matrixMap.put(constructor, matrix);
147 matrix.rows.add(row.replace(columnId, Expression.EMPTY_ARRAY));
149 else if(pattern instanceof EExternalConstant) {
150 EExternalConstant constant = (EExternalConstant)pattern;
151 Constant constructor = w.getModuleWriter().getExternalConstant(constant.getValue(), constant.getType());
153 ExpressionMatrix matrix = matrixMap.get(constructor);
155 CodeWriter newW = w.createBlock();
156 branches.add(new Branch(constructor, newW.getContinuation()));
157 matrix = new ExpressionMatrix(newW, replace(scrutinee, columnId, newW.getParameters()));
158 matrices.add(matrix);
159 matrixMap.put(constructor, matrix);
161 matrix.rows.add(row.replace(columnId, Expression.EMPTY_ARRAY));
164 throw new InternalCompilerError("Cannot handle an instance of " + pattern.getClass().getSimpleName() + " in a pattern.");
166 if(i < rows.size()) {
167 CodeWriter newW = w.createBlock();
168 ICont cont = newW.getContinuation();
169 branches.add(new Branch(null, cont));
170 split(newW, context, scrutinee, success, failure, rows.subList(i, rows.size()));
176 con = Types.getConstructor(scrutinee[columnId].getType());
177 } catch (MatchException e) {
178 throw new InternalCompilerError();
180 TypeConstructor cons = (TypeConstructor)context.environment.getTypeDescriptor(con);
181 int maxBranchCount = cons.isOpen ? Integer.MAX_VALUE
182 : cons.constructors.length;
183 if(branches.size() < maxBranchCount)
184 branches.add(new Branch(null, failure));
187 for(ExpressionMatrix mx : matrices)
188 split(mx.w, context, mx.scrutinee, success, failure, mx.rows);
189 w.switch_(scrutinee[columnId], branches.toArray(new Branch[branches.size()]));
192 private static void splitByViewPattern(CodeWriter w, CompilationContext context, IVal[] scrutinee, ICont success,
193 ICont failure, List<Row> rows, int viewPatternColumn) {
194 Row firstRow = rows.get(0);
195 EViewPattern firstViewPattern = (EViewPattern)firstRow.patterns[viewPatternColumn];
196 firstRow.patterns[viewPatternColumn] = firstViewPattern.pattern;
198 for(i=1;i<rows.size();++i) {
199 Row row = rows.get(i);
200 Expression pattern = row.patterns[viewPatternColumn];
202 if(pattern instanceof EApplyType)
203 pattern = ((EApplyType)pattern).getExpression();
204 else if(pattern instanceof EAsPattern) {
205 EAsPattern asPattern = (EAsPattern)pattern;
206 pattern = asPattern.getPattern();
207 asPattern.getVariable().setVal(scrutinee[viewPatternColumn]);
211 row.patterns[viewPatternColumn] = pattern;
213 if(!(pattern instanceof EViewPattern))
215 EViewPattern otherViewPattern = (EViewPattern)pattern;
216 if(!otherViewPattern.expression.equalsExpression(firstViewPattern.expression))
218 row.patterns[viewPatternColumn] = otherViewPattern.pattern;
221 IVal[] newScrutinee = Arrays.copyOf(scrutinee, scrutinee.length);
222 newScrutinee[viewPatternColumn] =
223 w.apply(firstViewPattern.location,
224 firstViewPattern.expression.toVal(context, w),
225 scrutinee[viewPatternColumn]);
226 if(i == rows.size()) {
227 split(w, context, newScrutinee, success, failure, rows);
230 CodeWriter cont = w.createBlock();
231 split(w, context, newScrutinee, success, cont.getContinuation(), rows.subList(0, i));
232 split(cont, context, scrutinee, success, failure, rows.subList(i, rows.size()));
236 public static void split(CodeWriter w, CompilationContext context, IVal[] scrutinee, ICont success, ICont failure, List<Row> rows) {
237 Row firstRow = rows.get(0);
238 Expression[] patterns = firstRow.patterns;
239 if(scrutinee.length != patterns.length)
240 throw new InternalCompilerError("Scrutinee and patterns have a different length");
242 // Find a non-variable pattern and split by it
243 int viewPatternColumn = -1;
244 for(int i=0;i<patterns.length;++i) {
245 Expression pattern = patterns[i];
246 if(pattern instanceof EViewPattern) {
247 if(viewPatternColumn == -1)
248 viewPatternColumn = i;
250 else if(!(pattern instanceof EVariable)) {
251 splitByConstructors(w, context, scrutinee, success, failure, rows, i);
256 if(viewPatternColumn >= 0) {
257 splitByViewPattern(w, context, scrutinee, success, failure, rows, viewPatternColumn);
261 // The first row has only variable patterns: no matching needed
262 for(int i=0;i<patterns.length;++i)
263 ((EVariable)patterns[i]).getVariable().setVal(scrutinee[i]);
264 if(firstRow.value instanceof GuardedExpressionGroup) {
265 GuardedExpressionGroup group = (GuardedExpressionGroup)firstRow.value;
266 if(rows.size() == 1) {
267 group.compile(context, w, success, failure);
270 CodeWriter newW = w.createBlock();
271 ICont cont = newW.getContinuation();
272 group.compile(context, w, success, cont);
273 split(newW, context, scrutinee, success, failure, rows.subList(1, rows.size()));
277 w.jump(success, firstRow.value.toVal(context, w));