1 package org.simantics.scl.compiler.elaboration.expressions;
3 import org.simantics.scl.compiler.constants.SCLConstructor;
4 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
5 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext.ExistentialFrame;
6 import org.simantics.scl.compiler.elaboration.expressions.records.FieldAssignment;
7 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
8 import org.simantics.scl.compiler.environment.AmbiguousNameException;
9 import org.simantics.scl.compiler.errors.Locations;
11 import gnu.trove.map.hash.THashMap;
13 public class ERecord extends ASTExpression {
15 public final EVar constructor;
16 public final FieldAssignment[] fields;
18 public ERecord(EVar constructor, FieldAssignment[] fields) {
19 this.constructor = constructor;
24 public Expression resolve(TranslationContext context) {
25 return resolve(context, false);
29 public Expression resolveAsPattern(TranslationContext context) {
30 return resolve(context, true);
33 public Expression resolve(TranslationContext context, boolean asPattern) {
34 SCLValue constructorValue;
36 constructorValue = context.getEnvironment().getLocalNamespace().getValue(constructor.name);
37 } catch (AmbiguousNameException e) {
38 context.getErrorLog().log(constructor.location, e.getMessage());
39 return new EError(constructor.location);
41 if(constructorValue == null) {
42 context.getErrorLog().log(constructor.location, "Couldn't resolve the record constructor " + constructor.name + ".");
43 return new EError(constructor.location);
45 if(!(constructorValue.getValue() instanceof SCLConstructor)) {
46 context.getErrorLog().log(constructor.location, "Value " + constructor.name + " is not a record constructor.");
47 return new EError(constructor.location);
49 String[] fieldNames = ((SCLConstructor)constructorValue.getValue()).recordFieldNames;
50 if(fieldNames == null) {
51 context.getErrorLog().log(constructor.location, "Value " + constructor.name + " is not a record constructor.");
52 return new EError(constructor.location);
54 Expression[] parameters = translateFieldsToFunctionParameters(context, fields, fieldNames);
55 if(parameters == null)
56 return new EError(location);
58 for(int i=0;i<parameters.length;++i) {
59 Expression parameter = parameters[i];
61 parameters[i] = Expressions.blank(null);
63 parameters[i] = parameter.resolveAsPattern(context);
66 boolean error = false;
67 for(int i=0;i<parameters.length;++i) {
68 Expression parameter = parameters[i];
69 if(parameter == null) {
70 ExistentialFrame frame = context.getCurrentExistentialFrame();
71 if(frame == null || frame.disallowNewExistentials) {
72 context.getErrorLog().log(location, "Field " + fieldNames[i] + " not defined.");
76 parameters[i] = frame.createBlank(location);
79 parameters[i] = parameter.resolve(context);
82 return new EError(location);
84 EApply result = new EApply(new EConstant(constructorValue), parameters);
85 result.setLocationDeep(location);
89 public static Expression[] translateFieldsToFunctionParameters(TranslationContext context, FieldAssignment[] fields, String[] fieldNames) {
90 THashMap<String,FieldAssignment> recordMap = new THashMap<String,FieldAssignment>(fields.length);
91 boolean error = false;
92 for(FieldAssignment field : fields) {
93 if(field.value == null) {
94 String actualName = field.name;
95 if(actualName.charAt(0) == '?')
96 actualName = actualName.substring(1);
97 String bestMatch = null;
98 int bestMatchLength = 0;
99 for(int i=0;i<fieldNames.length;++i) {
100 String fieldName = fieldNames[i];
101 if(actualName.startsWith(fieldName) && fieldName.length() > bestMatchLength) {
102 bestMatch = fieldName;
103 bestMatchLength = fieldName.length();
106 if(bestMatch == null) {
107 context.getErrorLog().log(field.location, "Invalid shorthand field " + field.name + " is defined twice.");
110 field.value = new EVar(field.location, field.name);
111 field.name = bestMatch;
113 if(recordMap.put(field.name, field) != null) {
114 context.getErrorLog().log(field.location, "Field " + field.name + " is defined more than once.");
120 Expression[] parameters = new Expression[fieldNames.length];
121 for(int i=0;i<fieldNames.length;++i) {
122 FieldAssignment assignment = recordMap.remove(fieldNames[i]);
123 if(assignment != null)
124 parameters[i] = assignment.value;
126 if(!recordMap.isEmpty()) {
127 for(FieldAssignment field : recordMap.values())
128 context.getErrorLog().log(field.location, "Field " + field.name + " is not defined in the constructor.");
135 public void setLocationDeep(long loc) {
136 if(location == Locations.NO_LOCATION) {
138 for(FieldAssignment field : fields)
139 if(field.value != null)
140 field.value.setLocationDeep(loc);
145 public Expression accept(ExpressionTransformer transformer) {
146 return transformer.transform(this);
150 public void accept(ExpressionVisitor visitor) {