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.expressions.records.FieldAssignment;
6 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
7 import org.simantics.scl.compiler.environment.AmbiguousNameException;
8 import org.simantics.scl.compiler.errors.Locations;
9 import org.simantics.scl.compiler.internal.parsing.Token;
11 import gnu.trove.map.hash.THashMap;
13 public class ERecord extends ASTExpression {
16 FieldAssignment[] fields;
18 public ERecord(Token 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.text);
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.text + ".");
43 return new EError(constructor.location);
45 if(!(constructorValue.getValue() instanceof SCLConstructor)) {
46 context.getErrorLog().log(constructor.location, "Value " + constructor.text + " 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.text + " is not a record constructor.");
52 return new EError(constructor.location);
54 THashMap<String,FieldAssignment> recordMap = new THashMap<String,FieldAssignment>(fields.length);
55 for(FieldAssignment field : fields) {
56 if(field.value == null) {
57 String bestMatch = null;
58 int bestMatchLength = 0;
59 for(int i=0;i<fieldNames.length;++i) {
60 String fieldName = fieldNames[i];
61 if(field.name.startsWith(fieldName) && fieldName.length() > bestMatchLength) {
62 bestMatch = fieldName;
63 bestMatchLength = fieldName.length();
66 if(bestMatch == null) {
67 context.getErrorLog().log(field.location, "Invalid shorthand field " + field.name + " is defined twice.");
68 return new EError(location);
70 field.value = new EVar(field.location, field.name);
71 field.name = bestMatch;
73 if(recordMap.put(field.name, field) != null) {
74 context.getErrorLog().log(field.location, "Field " + field.name + " is defined more than once.");
75 return new EError(location);
78 Expression[] parameters = new Expression[fieldNames.length];
79 boolean error = false;
80 for(int i=0;i<fieldNames.length;++i) {
81 FieldAssignment assignment = recordMap.remove(fieldNames[i]);
82 if(assignment == null) {
84 parameters[i] = Expressions.blank(null);
87 context.getErrorLog().log(location, "Field " + fieldNames[i] + " not defined.");
92 parameters[i] = asPattern
93 ? assignment.value.resolveAsPattern(context)
94 : assignment.value.resolve(context);
96 if(!recordMap.isEmpty()) {
97 for(FieldAssignment field : recordMap.values())
98 context.getErrorLog().log(field.location, "Field " + field.name + " is not defined in the constructor.");
102 return new EError(location);
104 EApply result = new EApply(new EConstant(constructorValue), parameters);
105 result.setLocationDeep(location);
111 public void setLocationDeep(long loc) {
112 if(location == Locations.NO_LOCATION) {
114 for(FieldAssignment field : fields)
115 if(field.value != null)
116 field.value.setLocationDeep(loc);
121 public Expression accept(ExpressionTransformer transformer) {
122 return transformer.transform(this);