1 package org.simantics.scl.compiler.elaboration.expressions;
3 import java.util.Arrays;
5 import org.simantics.scl.compiler.constants.SCLConstructor;
6 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
7 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext.ExistentialFrame;
8 import org.simantics.scl.compiler.elaboration.expressions.records.FieldAssignment;
9 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
10 import org.simantics.scl.compiler.environment.AmbiguousNameException;
11 import org.simantics.scl.compiler.errors.Locations;
13 import gnu.trove.map.hash.THashMap;
15 public class ERecord extends ASTExpression {
17 public static final boolean DEBUG = false;
19 public final EVar constructor;
20 public final FieldAssignment[] fields;
22 public ERecord(EVar constructor, FieldAssignment[] fields) {
23 this.constructor = constructor;
28 public Expression resolve(TranslationContext context) {
29 return resolve(context, false);
33 public Expression resolveAsPattern(TranslationContext context) {
34 return resolve(context, true);
37 public Expression resolve(TranslationContext context, boolean asPattern) {
38 SCLValue constructorValue;
40 constructorValue = context.resolveRecordConstructor(location, constructor.name);
41 } catch (AmbiguousNameException e) {
42 context.getErrorLog().log(constructor.location, e.getMessage());
43 return new EError(constructor.location);
45 if(constructorValue == null) {
46 context.getErrorLog().log(constructor.location, "Couldn't resolve the record constructor " + constructor.name + ".");
47 return new EError(constructor.location);
49 if(!(constructorValue.getValue() instanceof SCLConstructor)) {
50 context.getErrorLog().log(constructor.location, "Value " + constructor.name + " is not a record constructor.");
51 return new EError(constructor.location);
53 String[] fieldNames = ((SCLConstructor)constructorValue.getValue()).recordFieldNames;
54 if(fieldNames == null) {
55 context.getErrorLog().log(constructor.location, "Value " + constructor.name + " is not a record constructor.");
56 return new EError(constructor.location);
58 Expression[] parameters = translateFieldsToFunctionParameters(context, fields, fieldNames, false);
59 if(parameters == null)
60 return new EError(location);
62 for(int i=0;i<parameters.length;++i) {
63 Expression parameter = parameters[i];
65 parameters[i] = Expressions.blank(null);
67 parameters[i] = parameter.resolveAsPattern(context);
70 boolean error = false;
71 for(int i=0;i<parameters.length;++i) {
72 Expression parameter = parameters[i];
73 if(parameter == null) {
74 ExistentialFrame frame = context.getCurrentExistentialFrame();
75 if(frame == null || frame.disallowNewExistentials) {
76 context.getErrorLog().log(location, "Field " + fieldNames[i] + " not defined.");
80 parameters[i] = frame.createBlank(location);
83 parameters[i] = parameter.resolve(context);
86 return new EError(location);
88 EApply result = new EApply(new EConstant(constructorValue), parameters);
89 result.setLocationDeep(location);
93 public static Expression[] translateFieldsToFunctionParameters(TranslationContext context, FieldAssignment[] fields, String[] fieldNames, boolean chrLiteral) {
95 System.out.println("translateFieldsToFunctionParameters");
96 System.out.println(" fieldNames = " + Arrays.toString(fieldNames));
97 System.out.print(" fields = {");
98 for(int i=0;i<fields.length;++i) {
99 FieldAssignment field = fields[i];
101 System.out.print(", ");
102 System.out.print(field.name);
103 if(field.value != null) {
104 System.out.print(" = ");
105 System.out.print(field.value);
108 System.out.println("}");
111 THashMap<String,FieldAssignment> recordMap = new THashMap<String,FieldAssignment>(fields.length);
112 boolean error = false;
113 FieldAssignment wildcardField = null;
114 for(FieldAssignment field : fields) {
115 if(field.value == null) {
116 String actualName = field.name;
117 if(actualName.equals(FieldAssignment.WILDCARD)) {
118 if(wildcardField != null)
119 context.getErrorLog().log(field.location, "The record has more than one wildcard.");
120 wildcardField = field;
123 if(actualName.charAt(0) == '?')
124 actualName = actualName.substring(1);
125 String bestMatch = null;
126 int bestMatchLength = 0;
127 for(int i=0;i<fieldNames.length;++i) {
128 String fieldName = fieldNames[i];
129 if(actualName.startsWith(fieldName) && fieldName.length() > bestMatchLength) {
130 bestMatch = fieldName;
131 bestMatchLength = fieldName.length();
134 if(bestMatch == null) {
135 context.getErrorLog().log(field.location, "Invalid shorthand field " + field.name + " is defined twice.");
138 field.value = new EVar(field.location, field.name);
139 field.name = bestMatch;
141 if(recordMap.put(field.name, field) != null) {
142 context.getErrorLog().log(field.location, "Field " + field.name + " is defined more than once.");
148 Expression[] parameters = new Expression[fieldNames.length];
149 for(int i=0;i<fieldNames.length;++i) {
150 FieldAssignment assignment = recordMap.remove(fieldNames[i]);
151 if(assignment != null)
152 parameters[i] = assignment.value;
153 else if(wildcardField != null) {
154 String variableName = fieldNames[i];
156 variableName = "?" + variableName;
157 parameters[i] = new EVar(wildcardField.location, variableName);
160 if(!recordMap.isEmpty()) {
161 for(FieldAssignment field : recordMap.values())
162 context.getErrorLog().log(field.location, "Field " + field.name + " is not defined in the constructor.");
166 System.out.println(" => parameters = " + Arrays.toString(parameters));
171 public void setLocationDeep(long loc) {
172 if(location == Locations.NO_LOCATION) {
174 for(FieldAssignment field : fields)
175 if(field.value != null)
176 field.value.setLocationDeep(loc);
181 public Expression accept(ExpressionTransformer transformer) {
182 return transformer.transform(this);
186 public void accept(ExpressionVisitor visitor) {