]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/ERecord.java
(refs #7250) Support for record syntax for CHR relations
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / expressions / ERecord.java
1 package org.simantics.scl.compiler.elaboration.expressions;
2
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;
10
11 import gnu.trove.map.hash.THashMap;
12
13 public class ERecord extends ASTExpression {
14
15     public final EVar constructor;
16     public final FieldAssignment[] fields;
17     
18     public ERecord(EVar constructor, FieldAssignment[] fields) {
19         this.constructor = constructor;
20         this.fields = fields;
21     }
22
23     @Override
24     public Expression resolve(TranslationContext context) {
25         return resolve(context, false);
26     }
27     
28     @Override
29     public Expression resolveAsPattern(TranslationContext context) {
30         return resolve(context, true);
31     }
32     
33     public Expression resolve(TranslationContext context, boolean asPattern) {
34         SCLValue constructorValue; 
35         try {
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);
40         }
41         if(constructorValue == null) {
42             context.getErrorLog().log(constructor.location, "Couldn't resolve the record constructor " + constructor.name + ".");
43             return new EError(constructor.location);
44         }
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);
48         }
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);
53         }
54         Expression[] parameters = translateFieldsToFunctionParameters(context, fields, fieldNames);
55         if(parameters == null)
56             return new EError(location);
57         if(asPattern)
58             for(int i=0;i<parameters.length;++i) {
59                 Expression parameter = parameters[i];
60                 if(parameter == null)
61                     parameters[i] = Expressions.blank(null);
62                 else
63                     parameters[i] = parameter.resolveAsPattern(context);
64             }
65         else {
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.");
73                         error = true;
74                     }
75                     else
76                         parameters[i] = frame.createBlank(location); 
77                 }
78                 else
79                     parameters[i] = parameter.resolve(context);
80             }
81             if(error)
82                 return new EError(location);
83         }
84         EApply result = new EApply(new EConstant(constructorValue), parameters);
85         result.setLocationDeep(location);
86         return result;
87     }
88     
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();
104                     }
105                 }
106                 if(bestMatch == null) {
107                     context.getErrorLog().log(field.location, "Invalid shorthand field " + field.name + " is defined twice.");
108                     error = true;
109                 }
110                 field.value = new EVar(field.location, field.name);
111                 field.name = bestMatch;
112             }
113             if(recordMap.put(field.name, field) != null) {
114                 context.getErrorLog().log(field.location, "Field " + field.name + " is defined more than once.");
115                 error = true;
116             }
117         }
118         if(error)
119             return null;
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;
125         }
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.");
129             return null;
130         }
131         return parameters;
132     }
133
134     @Override
135     public void setLocationDeep(long loc) {
136         if(location == Locations.NO_LOCATION) {
137             location = loc;
138             for(FieldAssignment field : fields)
139                 if(field.value != null)
140                     field.value.setLocationDeep(loc);
141         }
142     }
143     
144     @Override
145     public Expression accept(ExpressionTransformer transformer) {
146         return transformer.transform(this);
147     }
148     
149     @Override
150     public void accept(ExpressionVisitor visitor) {
151         visitor.visit(this);
152     }
153 }